/Users/kiltum/projects/zxcpp/include/memory.hpp
Line
Count
Source
1
#ifndef MEMORY_HPP
2
#define MEMORY_HPP
3
4
#include <cstdint>
5
6
class Memory
7
{
8
private:
9
    uint8_t bank[8][16384]; // banks of memory
10
    uint8_t rom[2][16384];  // banks of ROMs
11
    bool is48;              // machine version
12
    uint8_t bankMapping[4]; // Which bank mapped now
13
    bool ULAShadow;
14
15
public:
16
    // Constructor
17
    Memory();
18
19
    // Read a byte from memory
20
    uint8_t ReadByte(uint16_t address);
21
    // Special function for ULA reading screen (main or shadow)
22
    uint8_t ULAReadByte(uint16_t address);
23
    // Write a byte to memory
24
    void WriteByte(uint16_t address, uint8_t value);
25
26
    // Load 48k rom to memory
27
    void Read48(void);
28
    // load 128 rom to memory
29
    void Read128(void);
30
    // Load diag rom to memory
31
    void ReadDiag(void);
32
    // load another diag rom
33
    void ReadDiag2(void);
34
35
    // true, is we emulate 48k. No banks, no any reaction to write to 7FFD. false - we emulate 128k
36
    void change48(bool is48s);
37
    void writePort(uint16_t port, uint8_t value); // handler for 7ffd
38
0
    bool getIs48() const { return is48; }         // Getter for is48 flag
39
    bool canWriteRom;                             // Can we overwrite ROM, as in Baltika version? Its public, because test need it
40
};
41
42
#endif // MEMORY_HPP
/Users/kiltum/projects/zxcpp/include/z80.hpp
Line
Count
Source
1
#ifndef Z80_HPP
2
#define Z80_HPP
3
4
#include <cstdint>
5
#include "memory.hpp"
6
#include "port.hpp"
7
8
// Z80 Flag Definitions
9
47.4M
#define FLAG_S 0x80  // Sign Flag (S) - bit 7
10
72.7M
#define FLAG_Z 0x40  // Zero Flag (Z) - bit 6
11
117M
#define FLAG_Y 0x20  // Bit 5 (Y) - bit 5
12
60.3M
#define FLAG_H 0x10  // Half Carry Flag (H) - bit 4
13
117M
#define FLAG_X 0x08  // Bit 3 (X) - bit 3
14
47.4M
#define FLAG_PV 0x04 // Parity/Overflow Flag (P/V) - bit 2
15
60.3M
#define FLAG_N 0x02  // Add/Subtract Flag (N) - bit 1
16
65.8M
#define FLAG_C 0x01  // Carry Flag (C) - bit 0
17
18
class Z80
19
{
20
public:
21
    Memory *memory; // Pointer to memory instance
22
    Port *port;     // Pointer to port instance
23
24
    // Main register set
25
    union
26
    {
27
        struct
28
        {
29
            uint8_t F;
30
            uint8_t A;
31
        };
32
        uint16_t AF;
33
    };
34
35
    union
36
    {
37
        struct
38
        {
39
            uint8_t C;
40
            uint8_t B;
41
        };
42
        uint16_t BC;
43
    };
44
45
    union
46
    {
47
        struct
48
        {
49
            uint8_t E;
50
            uint8_t D;
51
        };
52
        uint16_t DE;
53
    };
54
55
    union
56
    {
57
        struct
58
        {
59
            uint8_t L;
60
            uint8_t H;
61
        };
62
        uint16_t HL;
63
    };
64
65
    // Shadow register set (alternates)
66
    union
67
    {
68
        struct
69
        {
70
            uint8_t F_;
71
            uint8_t A_;
72
        };
73
        uint16_t AF_;
74
    };
75
76
    union
77
    {
78
        struct
79
        {
80
            uint8_t C_;
81
            uint8_t B_;
82
        };
83
        uint16_t BC_;
84
    };
85
86
    union
87
    {
88
        struct
89
        {
90
            uint8_t E_;
91
            uint8_t D_;
92
        };
93
        uint16_t DE_;
94
    };
95
96
    union
97
    {
98
        struct
99
        {
100
            uint8_t L_;
101
            uint8_t H_;
102
        };
103
        uint16_t HL_;
104
    };
105
106
    // Index registers
107
    uint16_t IX;
108
    uint16_t IY;
109
110
    // Internal registers
111
    uint16_t SP;     // Stack Pointer
112
    uint16_t PC;     // Program Counter
113
    uint8_t I;       // Interrupt vector
114
    uint8_t R;       // Refresh counter
115
    uint16_t MEMPTR; // Memory pointer (internal use)
116
117
    // Interrupt-related registers and flags
118
    bool IFF1;             // Interrupt Flip Flop 1
119
    bool IFF2;             // Interrupt Flip Flop 2
120
    uint8_t IM;            // Interrupt Mode (0, 1, or 2)
121
    bool HALT;             // HALT state flag
122
    bool InterruptPending; // Interrupt pending flag
123
124
    // Constructor
125
    Z80(Memory *mem, Port *port);
126
127
    // Execute one instruction and return number of ticks consumed
128
    int ExecuteOneInstruction();
129
130
    // Handle interrupt processing
131
    int HandleInterrupt();
132
    bool isNMOS;    // cpu type NMOS (true, default) or Zilog/SGS (false)
133
    void NMI(void); // Non Maskable Interrupt
134
135
private:
136
    // Flag update functions
137
    void UpdateSZFlags(uint8_t result);
138
    void UpdatePVFlags(uint8_t result);
139
    void UpdateSZXYPVFlags(uint8_t result);
140
    void UpdateFlags3and5FromValue(uint8_t value);
141
    void UpdateFlags3and5FromAddress(uint16_t address);
142
    void UpdateSZXYFlags(uint8_t result);
143
    void UpdateXYFlags(uint8_t result);
144
145
    // Flag manipulation functions
146
    bool GetFlag(uint8_t flag);
147
    void SetFlag(uint8_t flag, bool state);
148
    void ClearFlag(uint8_t flag);
149
    void ClearAllFlags();
150
151
    // Helper functions
152
    uint8_t ReadImmediateByte();
153
    uint16_t ReadImmediateWord();
154
    int8_t ReadDisplacement();
155
    uint8_t ReadOpcode();
156
    void Push(uint16_t value);
157
    uint16_t Pop();
158
159
    // Opcode helper functions
160
    uint8_t inc8(uint8_t value);
161
    uint8_t dec8(uint8_t value);
162
    void rlca();
163
    void rla();
164
    void rrca();
165
    void rra();
166
    void daa();
167
    bool parity(uint8_t val);
168
    void cpl();
169
    void scf();
170
    void ccf();
171
    uint16_t add16(uint16_t a, uint16_t b);
172
    void add8(uint8_t value);
173
    void adc8(uint8_t value);
174
    void sub8(uint8_t value);
175
    void sbc8(uint8_t value);
176
    void and8(uint8_t value);
177
    void xor8(uint8_t value);
178
    void or8(uint8_t value);
179
    void cp8(uint8_t value);
180
181
    // CB opcode helper functions
182
    uint8_t rlc(uint8_t value);
183
    uint8_t rrc(uint8_t value);
184
    uint8_t rl(uint8_t value);
185
    uint8_t rr(uint8_t value);
186
    uint8_t sla(uint8_t value);
187
    uint8_t sra(uint8_t value);
188
    uint8_t sll(uint8_t value);
189
    uint8_t srl(uint8_t value);
190
    void bit(uint8_t bitNum, uint8_t value);
191
    void bitMem(uint8_t bitNum, uint8_t value, uint8_t addrHi);
192
    uint8_t res(uint8_t bitNum, uint8_t value);
193
    uint8_t set(uint8_t bitNum, uint8_t value);
194
195
    // DD opcode helper functions
196
    uint16_t add16IX(uint16_t a, uint16_t b);
197
    uint8_t GetIXH();
198
    uint8_t GetIXL();
199
    void SetIXH(uint8_t value);
200
    void SetIXL(uint8_t value);
201
    int executeIncDecIndexed(bool isInc);
202
    int executeLoadFromIndexed(uint8_t reg);
203
    int executeStoreToIndexed(uint8_t value);
204
    int executeALUIndexed(uint8_t opType);
205
    int executeDDCBOpcode();
206
207
    // FD opcode helper functions
208
    uint16_t add16IY(uint16_t a, uint16_t b);
209
    uint8_t GetIYH();
210
    uint8_t GetIYL();
211
    void SetIYH(uint8_t value);
212
    void SetIYL(uint8_t value);
213
    int executeIncDecIndexedIY(bool isInc);
214
    int executeLoadFromIndexedIY(uint8_t reg);
215
    int executeStoreToIndexedIY(uint8_t value);
216
    int executeALUIndexedIY(uint8_t opType);
217
218
    // FDCB opcode helper functions
219
    int executeRotateShiftIndexedIY(uint8_t opcode, uint16_t addr, uint8_t value);
220
    int executeResetBitIndexedIY(uint8_t opcode, uint16_t addr, uint8_t value);
221
    int executeSetBitIndexedIY(uint8_t opcode, uint16_t addr, uint8_t value);
222
223
    // ED opcode helper functions
224
    uint16_t sbc16(uint16_t val1, uint16_t val2);
225
    uint16_t sbc16WithMEMPTR(uint16_t a, uint16_t b);
226
    uint16_t adc16(uint16_t val1, uint16_t val2);
227
    uint16_t adc16WithMEMPTR(uint16_t a, uint16_t b);
228
    void neg();
229
    void retn();
230
    void reti();
231
    void ldAI();
232
    void ldAR();
233
    void rrd();
234
    void rld();
235
    int executeIN(uint8_t reg);
236
    int executeOUT(uint8_t reg);
237
    void ldi();
238
    void cpi();
239
    void ini();
240
    void outi();
241
    void ldd();
242
    void cpd();
243
    void ind();
244
    void outd();
245
    int ldir();
246
    int cpir();
247
    int inir();
248
    int otir();
249
    int lddr();
250
    int cpdr();
251
    int indr();
252
    int otdr();
253
    uint8_t inC();
254
    void outC(uint8_t value);
255
256
    int ExecuteOpcode();
257
    int ExecuteCBOpcode();
258
    int ExecuteDDOpcode();
259
    int ExecuteEDOpcode();
260
    int ExecuteFDCBOpcode();
261
    int ExecuteFDOpcode();
262
};
263
264
#endif // Z80_HPP
/Users/kiltum/projects/zxcpp/lib/imgui/backends/imgui_impl_sdl3.cpp
Line
Count
Source
1
// dear imgui: Platform Backend for SDL3
2
// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
3
// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
4
5
// Implemented features:
6
//  [X] Platform: Clipboard support.
7
//  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
8
//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
9
//  [X] Platform: Gamepad support.
10
//  [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
11
//  [X] Platform: IME support.
12
13
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
14
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
15
// Learn about Dear ImGui:
16
// - FAQ                  https://dearimgui.com/faq
17
// - Getting Started      https://dearimgui.com/getting-started
18
// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).
19
// - Introduction, links and more at the top of imgui.cpp
20
21
// CHANGELOG
22
// (minor and older changes stripped away, please see git history for details)
23
//  2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_EVENT_MOUSE_MOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786)
24
//  2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown.
25
//  2025-09-15: Use SDL_GetWindowDisplayScale() on Mac to output DisplayFrameBufferScale. The function is more reliable during resolution changes e.g. going fullscreen. (#8703, #4414)
26
//  2025-06-27: IME: avoid calling SDL_StartTextInput() again if already active. (#8727)
27
//  2025-04-22: IME: honor ImGuiPlatformImeData->WantTextInput as an alternative way to call SDL_StartTextInput(), without IME being necessarily visible.
28
//  2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561)
29
//  2025-03-30: Update for SDL3 api changes: Revert SDL_GetClipboardText() memory ownership change. (#8530, #7801)
30
//  2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set.
31
//  2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468)
32
//  2025-02-26: Only start SDL_CaptureMouse() when mouse is being dragged, to mitigate issues with e.g.Linux debuggers not claiming capture back. (#6410, #3650)
33
//  2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode.
34
//  2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
35
//  2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler.
36
//  2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array.
37
//  2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten.
38
//  2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
39
//  2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
40
//               - io.GetClipboardTextFn    -> platform_io.Platform_GetClipboardTextFn
41
//               - io.SetClipboardTextFn    -> platform_io.Platform_SetClipboardTextFn
42
//               - io.PlatformSetImeDataFn  -> platform_io.Platform_SetImeDataFn
43
//  2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
44
//  2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
45
//  2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807)
46
//  2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801)
47
//  2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794)
48
//  2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762).
49
//  2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea().
50
//  2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures.
51
//  2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents.
52
//  2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames.
53
//  2024-05-15: Update for SDL3 api changes: SDLK_ renames.
54
//  2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME.
55
//  2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode().
56
//  2023-11-13: Updated for recent SDL3 API changes.
57
//  2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
58
//  2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391)
59
//  2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
60
//  2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
61
//  2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
62
//  2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog.
63
64
#include "imgui.h"
65
#ifndef IMGUI_DISABLE
66
#include "imgui_impl_sdl3.h"
67
68
// Clang warnings with -Weverything
69
#if defined(__clang__)
70
#pragma clang diagnostic push
71
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast
72
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
73
#endif
74
75
// SDL
76
#include <SDL3/SDL.h>
77
#include <stdio.h>              // for snprintf()
78
#if defined(__APPLE__)
79
#include <TargetConditionals.h>
80
#endif
81
#ifdef _WIN32
82
#ifndef WIN32_LEAN_AND_MEAN
83
#define WIN32_LEAN_AND_MEAN
84
#endif
85
#include <windows.h>
86
#endif
87
88
#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
89
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE    1
90
#else
91
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE    0
92
#endif
93
94
// FIXME-LEGACY: remove when SDL 3.1.3 preview is released.
95
#ifndef SDLK_APOSTROPHE
96
#define SDLK_APOSTROPHE SDLK_QUOTE
97
#endif
98
#ifndef SDLK_GRAVE
99
#define SDLK_GRAVE SDLK_BACKQUOTE
100
#endif
101
102
// SDL Data
103
struct ImGui_ImplSDL3_Data
104
{
105
    SDL_Window*             Window;
106
    SDL_WindowID            WindowID;
107
    SDL_Renderer*           Renderer;
108
    Uint64                  Time;
109
    char*                   ClipboardTextData;
110
    char                    BackendPlatformName[48];
111
112
    // IME handling
113
    SDL_Window*             ImeWindow;
114
115
    // Mouse handling
116
    Uint32                  MouseWindowID;
117
    int                     MouseButtonsDown;
118
    SDL_Cursor*             MouseCursors[ImGuiMouseCursor_COUNT];
119
    SDL_Cursor*             MouseLastCursor;
120
    int                     MousePendingLeaveFrame;
121
    bool                    MouseCanUseGlobalState;
122
    bool                    MouseCanUseCapture;
123
124
    // Gamepad handling
125
    ImVector<SDL_Gamepad*>      Gamepads;
126
    ImGui_ImplSDL3_GamepadMode  GamepadMode;
127
    bool                        WantUpdateGamepadsList;
128
129
1
    ImGui_ImplSDL3_Data()   { memset((void*)this, 0, sizeof(*this)); }
130
};
131
132
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
133
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
134
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
135
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
136
static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
137
325k
{
138
325k
    return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
139
325k
}
140
141
// Functions
142
static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
143
0
{
144
0
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
145
0
    if (bd->ClipboardTextData)
146
0
        SDL_free(bd->ClipboardTextData);
147
0
    bd->ClipboardTextData = SDL_GetClipboardText();
148
0
    return bd->ClipboardTextData;
149
0
}
150
151
static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text)
152
0
{
153
0
    SDL_SetClipboardText(text);
154
0
}
155
156
static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
157
0
{
158
0
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
159
0
    SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
160
0
    SDL_Window* window = SDL_GetWindowFromID(window_id);
161
0
    if ((!(data->WantVisible || data->WantTextInput) || bd->ImeWindow != window) && bd->ImeWindow != nullptr)
162
0
    {
163
0
        SDL_StopTextInput(bd->ImeWindow);
164
0
        bd->ImeWindow = nullptr;
165
0
    }
166
0
    if (data->WantVisible)
167
0
    {
168
0
        SDL_Rect r;
169
0
        r.x = (int)data->InputPos.x;
170
0
        r.y = (int)data->InputPos.y;
171
0
        r.w = 1;
172
0
        r.h = (int)data->InputLineHeight;
173
0
        SDL_SetTextInputArea(window, &r, 0);
174
0
        bd->ImeWindow = window;
175
0
    }
176
0
    if (!SDL_TextInputActive(window) && (data->WantVisible || data->WantTextInput))
177
0
        SDL_StartTextInput(window);
178
0
}
179
180
// Not static to allow third-party code to use that if they want to (but undocumented)
181
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
182
ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
183
1.49k
{
184
    // Keypad doesn't have individual key values in SDL3
185
1.49k
    switch (scancode)
186
1.49k
    {
187
0
        case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0;
188
0
        case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1;
189
0
        case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2;
190
0
        case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3;
191
0
        case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4;
192
0
        case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5;
193
0
        case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6;
194
0
        case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7;
195
0
        case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8;
196
0
        case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9;
197
0
        case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal;
198
0
        case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide;
199
0
        case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
200
0
        case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract;
201
0
        case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd;
202
0
        case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter;
203
0
        case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual;
204
1.49k
        default: break;
205
1.49k
    }
206
1.49k
    switch (keycode)
207
1.49k
    {
208
0
        case SDLK_TAB: return ImGuiKey_Tab;
209
0
        case SDLK_LEFT: return ImGuiKey_LeftArrow;
210
564
        case SDLK_RIGHT: return ImGuiKey_RightArrow;
211
394
        case SDLK_UP: return ImGuiKey_UpArrow;
212
313
        case SDLK_DOWN: return ImGuiKey_DownArrow;
213
0
        case SDLK_PAGEUP: return ImGuiKey_PageUp;
214
0
        case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
215
0
        case SDLK_HOME: return ImGuiKey_Home;
216
0
        case SDLK_END: return ImGuiKey_End;
217
0
        case SDLK_INSERT: return ImGuiKey_Insert;
218
0
        case SDLK_DELETE: return ImGuiKey_Delete;
219
0
        case SDLK_BACKSPACE: return ImGuiKey_Backspace;
220
0
        case SDLK_SPACE: return ImGuiKey_Space;
221
2
        case SDLK_RETURN: return ImGuiKey_Enter;
222
0
        case SDLK_ESCAPE: return ImGuiKey_Escape;
223
        //case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe;
224
0
        case SDLK_COMMA: return ImGuiKey_Comma;
225
        //case SDLK_MINUS: return ImGuiKey_Minus;
226
0
        case SDLK_PERIOD: return ImGuiKey_Period;
227
        //case SDLK_SLASH: return ImGuiKey_Slash;
228
0
        case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
229
        //case SDLK_EQUALS: return ImGuiKey_Equal;
230
        //case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
231
        //case SDLK_BACKSLASH: return ImGuiKey_Backslash;
232
        //case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
233
        //case SDLK_GRAVE: return ImGuiKey_GraveAccent;
234
0
        case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
235
0
        case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
236
0
        case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
237
0
        case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
238
0
        case SDLK_PAUSE: return ImGuiKey_Pause;
239
0
        case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
240
0
        case SDLK_LSHIFT: return ImGuiKey_LeftShift;
241
216
        case SDLK_LALT: return ImGuiKey_LeftAlt;
242
0
        case SDLK_LGUI: return ImGuiKey_LeftSuper;
243
0
        case SDLK_RCTRL: return ImGuiKey_RightCtrl;
244
0
        case SDLK_RSHIFT: return ImGuiKey_RightShift;
245
0
        case SDLK_RALT: return ImGuiKey_RightAlt;
246
0
        case SDLK_RGUI: return ImGuiKey_RightSuper;
247
0
        case SDLK_APPLICATION: return ImGuiKey_Menu;
248
0
        case SDLK_0: return ImGuiKey_0;
249
2
        case SDLK_1: return ImGuiKey_1;
250
0
        case SDLK_2: return ImGuiKey_2;
251
0
        case SDLK_3: return ImGuiKey_3;
252
0
        case SDLK_4: return ImGuiKey_4;
253
2
        case SDLK_5: return ImGuiKey_5;
254
0
        case SDLK_6: return ImGuiKey_6;
255
0
        case SDLK_7: return ImGuiKey_7;
256
0
        case SDLK_8: return ImGuiKey_8;
257
0
        case SDLK_9: return ImGuiKey_9;
258
0
        case SDLK_A: return ImGuiKey_A;
259
0
        case SDLK_B: return ImGuiKey_B;
260
0
        case SDLK_C: return ImGuiKey_C;
261
0
        case SDLK_D: return ImGuiKey_D;
262
0
        case SDLK_E: return ImGuiKey_E;
263
0
        case SDLK_F: return ImGuiKey_F;
264
0
        case SDLK_G: return ImGuiKey_G;
265
0
        case SDLK_H: return ImGuiKey_H;
266
0
        case SDLK_I: return ImGuiKey_I;
267
0
        case SDLK_J: return ImGuiKey_J;
268
0
        case SDLK_K: return ImGuiKey_K;
269
0
        case SDLK_L: return ImGuiKey_L;
270
0
        case SDLK_M: return ImGuiKey_M;
271
0
        case SDLK_N: return ImGuiKey_N;
272
0
        case SDLK_O: return ImGuiKey_O;
273
0
        case SDLK_P: return ImGuiKey_P;
274
0
        case SDLK_Q: return ImGuiKey_Q;
275
0
        case SDLK_R: return ImGuiKey_R;
276
0
        case SDLK_S: return ImGuiKey_S;
277
0
        case SDLK_T: return ImGuiKey_T;
278
0
        case SDLK_U: return ImGuiKey_U;
279
0
        case SDLK_V: return ImGuiKey_V;
280
0
        case SDLK_W: return ImGuiKey_W;
281
0
        case SDLK_X: return ImGuiKey_X;
282
0
        case SDLK_Y: return ImGuiKey_Y;
283
0
        case SDLK_Z: return ImGuiKey_Z;
284
0
        case SDLK_F1: return ImGuiKey_F1;
285
0
        case SDLK_F2: return ImGuiKey_F2;
286
0
        case SDLK_F3: return ImGuiKey_F3;
287
0
        case SDLK_F4: return ImGuiKey_F4;
288
0
        case SDLK_F5: return ImGuiKey_F5;
289
0
        case SDLK_F6: return ImGuiKey_F6;
290
0
        case SDLK_F7: return ImGuiKey_F7;
291
0
        case SDLK_F8: return ImGuiKey_F8;
292
0
        case SDLK_F9: return ImGuiKey_F9;
293
0
        case SDLK_F10: return ImGuiKey_F10;
294
0
        case SDLK_F11: return ImGuiKey_F11;
295
0
        case SDLK_F12: return ImGuiKey_F12;
296
0
        case SDLK_F13: return ImGuiKey_F13;
297
0
        case SDLK_F14: return ImGuiKey_F14;
298
0
        case SDLK_F15: return ImGuiKey_F15;
299
0
        case SDLK_F16: return ImGuiKey_F16;
300
0
        case SDLK_F17: return ImGuiKey_F17;
301
0
        case SDLK_F18: return ImGuiKey_F18;
302
0
        case SDLK_F19: return ImGuiKey_F19;
303
0
        case SDLK_F20: return ImGuiKey_F20;
304
0
        case SDLK_F21: return ImGuiKey_F21;
305
0
        case SDLK_F22: return ImGuiKey_F22;
306
0
        case SDLK_F23: return ImGuiKey_F23;
307
0
        case SDLK_F24: return ImGuiKey_F24;
308
0
        case SDLK_AC_BACK: return ImGuiKey_AppBack;
309
0
        case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
310
0
        default: break;
311
1.49k
    }
312
313
    // Fallback to scancode
314
0
    switch (scancode)
315
0
    {
316
0
    case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent;
317
0
    case SDL_SCANCODE_MINUS: return ImGuiKey_Minus;
318
0
    case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal;
319
0
    case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket;
320
0
    case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket;
321
0
    case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102;
322
0
    case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash;
323
0
    case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon;
324
0
    case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
325
0
    case SDL_SCANCODE_COMMA: return ImGuiKey_Comma;
326
0
    case SDL_SCANCODE_PERIOD: return ImGuiKey_Period;
327
0
    case SDL_SCANCODE_SLASH: return ImGuiKey_Slash;
328
0
    default: break;
329
0
    }
330
0
    return ImGuiKey_None;
331
0
}
332
333
static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
334
1.49k
{
335
1.49k
    ImGuiIO& io = ImGui::GetIO();
336
1.49k
    io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
337
1.49k
    io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
338
1.49k
    io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
339
1.49k
    io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
340
1.49k
}
341
342
343
static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id)
344
1.62k
{
345
1.62k
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
346
1.62k
    return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr;
347
1.62k
}
348
349
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
350
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
351
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
352
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
353
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
354
bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
355
1.63k
{
356
1.63k
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
357
1.63k
    IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
358
1.63k
    ImGuiIO& io = ImGui::GetIO();
359
360
1.63k
    switch (event->type)
361
1.63k
    {
362
124
        case SDL_EVENT_MOUSE_MOTION:
363
124
        {
364
124
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr)
365
0
                return false;
366
124
            ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
367
124
            io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
368
124
            io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
369
124
            return true;
370
124
        }
371
0
        case SDL_EVENT_MOUSE_WHEEL:
372
0
        {
373
0
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr)
374
0
                return false;
375
            //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
376
0
            float wheel_x = -event->wheel.x;
377
0
            float wheel_y = event->wheel.y;
378
0
            io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
379
0
            io.AddMouseWheelEvent(wheel_x, wheel_y);
380
0
            return true;
381
0
        }
382
2
        case SDL_EVENT_MOUSE_BUTTON_DOWN:
383
4
        case SDL_EVENT_MOUSE_BUTTON_UP:
384
4
        {
385
4
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr)
386
0
                return false;
387
4
            int mouse_button = -1;
388
4
            if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
389
4
            if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
390
4
            if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
391
4
            if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
392
4
            if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
393
4
            if (mouse_button == -1)
394
0
                break;
395
4
            io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
396
4
            io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
397
4
            bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
398
4
            return true;
399
4
        }
400
0
        case SDL_EVENT_TEXT_INPUT:
401
0
        {
402
0
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr)
403
0
                return false;
404
0
            io.AddInputCharactersUTF8(event->text.text);
405
0
            return true;
406
0
        }
407
1.33k
        case SDL_EVENT_KEY_DOWN:
408
1.49k
        case SDL_EVENT_KEY_UP:
409
1.49k
        {
410
1.49k
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID) == nullptr)
411
0
                return false;
412
1.49k
            ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod);
413
            //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=%d ('%s'), scancode=%d ('%s'), mod=%X\n",
414
            //    (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP  ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod);
415
1.49k
            ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
416
1.49k
            io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
417
1.49k
            io.SetKeyEventNativeData(key, (int)event->key.key, (int)event->key.scancode, (int)event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
418
1.49k
            return true;
419
1.49k
        }
420
2
        case SDL_EVENT_WINDOW_MOUSE_ENTER:
421
2
        {
422
2
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
423
0
                return false;
424
2
            bd->MouseWindowID = event->window.windowID;
425
2
            bd->MousePendingLeaveFrame = 0;
426
2
            return true;
427
2
        }
428
        // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
429
        //   causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
430
        //   we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
431
        // FIXME: Unconfirmed whether this is still needed with SDL3.
432
2
        case SDL_EVENT_WINDOW_MOUSE_LEAVE:
433
2
        {
434
2
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
435
0
                return false;
436
2
            bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1;
437
2
            return true;
438
2
        }
439
1
        case SDL_EVENT_WINDOW_FOCUS_GAINED:
440
1
        case SDL_EVENT_WINDOW_FOCUS_LOST:
441
1
        {
442
1
            if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
443
0
                return false;
444
1
            io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
445
1
            return true;
446
1
        }
447
0
        case SDL_EVENT_GAMEPAD_ADDED:
448
0
        case SDL_EVENT_GAMEPAD_REMOVED:
449
0
        {
450
0
            bd->WantUpdateGamepadsList = true;
451
0
            return true;
452
0
        }
453
8
        default:
454
8
            break;
455
1.63k
    }
456
8
    return false;
457
1.63k
}
458
459
static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window)
460
1
{
461
1
    viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window);
462
1
    viewport->PlatformHandleRaw = nullptr;
463
#if defined(_WIN32) && !defined(__WINRT__)
464
    viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
465
#elif defined(__APPLE__)
466
1
    viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
467
1
#endif
468
1
}
469
470
static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
471
1
{
472
1
    ImGuiIO& io = ImGui::GetIO();
473
1
    IMGUI_CHECKVERSION();
474
1
    IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
475
1
    IM_UNUSED(sdl_gl_context); // Unused in this branch
476
477
1
    const int ver_linked = SDL_GetVersion();
478
479
    // Setup backend capabilities flags
480
1
    ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
481
1
    snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%d.%d.%d; %d.%d.%d)",
482
1
        SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
483
1
    io.BackendPlatformUserData = (void*)bd;
484
1
    io.BackendPlatformName = bd->BackendPlatformName;
485
1
    io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;           // We can honor GetMouseCursor() values (optional)
486
1
    io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;            // We can honor io.WantSetMousePos requests (optional, rarely used)
487
488
1
    bd->Window = window;
489
1
    bd->WindowID = SDL_GetWindowID(window);
490
1
    bd->Renderer = renderer;
491
492
    // Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
493
    // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
494
1
    bd->MouseCanUseGlobalState = false;
495
1
    bd->MouseCanUseCapture = false;
496
1
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
497
1
    const char* sdl_backend = SDL_GetCurrentVideoDriver();
498
1
    const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
499
1
    for (const char* item : capture_and_global_state_whitelist)
500
5
        if (strncmp(sdl_backend, item, strlen(item)) == 0)
501
1
            bd->MouseCanUseGlobalState = bd->MouseCanUseCapture = true;
502
1
#endif
503
504
1
    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
505
1
    platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
506
1
    platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
507
1
    platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
508
1
    platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url) == 0; };
509
510
    // Gamepad handling
511
1
    bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
512
1
    bd->WantUpdateGamepadsList = true;
513
514
    // Load mouse cursors
515
1
    bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
516
1
    bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT);
517
1
    bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE);
518
1
    bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
519
1
    bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
520
1
    bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE);
521
1
    bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE);
522
1
    bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER);
523
1
    bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
524
1
    bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_PROGRESS);
525
1
    bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED);
526
527
    // Set platform dependent data in viewport
528
    // Our mouse update function expect PlatformHandle to be filled for the main viewport
529
1
    ImGuiViewport* main_viewport = ImGui::GetMainViewport();
530
1
    ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window);
531
532
    // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
533
    // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
534
    // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
535
    // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
536
    // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
537
1
#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
538
1
    SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
539
1
#endif
540
541
    // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
542
1
#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
543
1
    SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
544
1
#endif
545
546
1
    return true;
547
1
}
548
549
bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
550
0
{
551
0
    IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
552
0
    return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context);
553
0
}
554
555
bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window)
556
0
{
557
0
    return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
558
0
}
559
560
bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window)
561
0
{
562
0
#if !defined(_WIN32)
563
0
    IM_ASSERT(0 && "Unsupported");
564
0
#endif
565
0
    return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
566
0
}
567
568
bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window)
569
0
{
570
0
    return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
571
0
}
572
573
bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
574
1
{
575
1
    return ImGui_ImplSDL3_Init(window, renderer, nullptr);
576
1
}
577
578
bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window)
579
0
{
580
0
    return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
581
0
}
582
583
bool ImGui_ImplSDL3_InitForOther(SDL_Window* window)
584
0
{
585
0
    return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
586
0
}
587
588
static void ImGui_ImplSDL3_CloseGamepads();
589
590
void ImGui_ImplSDL3_Shutdown()
591
1
{
592
1
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
593
1
    IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
594
1
    ImGuiIO& io = ImGui::GetIO();
595
1
    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
596
597
1
    if (bd->ClipboardTextData)
598
0
        SDL_free(bd->ClipboardTextData);
599
12
    for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
600
11
        SDL_DestroyCursor(bd->MouseCursors[cursor_n]);
601
1
    ImGui_ImplSDL3_CloseGamepads();
602
603
1
    io.BackendPlatformName = nullptr;
604
1
    io.BackendPlatformUserData = nullptr;
605
1
    io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);
606
1
    platform_io.ClearPlatformHandlers();
607
1
    IM_DELETE(bd);
608
1
}
609
610
static void ImGui_ImplSDL3_UpdateMouseData()
611
80.5k
{
612
80.5k
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
613
80.5k
    ImGuiIO& io = ImGui::GetIO();
614
615
    // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
616
80.5k
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
617
    // - SDL_CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside.
618
    // - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue we wait until mouse has moved to begin capture.
619
80.5k
    if (bd->MouseCanUseCapture)
620
80.5k
    {
621
80.5k
        bool want_capture = false;
622
483k
        for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++)
623
402k
            if (ImGui::IsMouseDragging(button_n, 1.0f))
624
0
                want_capture = true;
625
80.5k
        SDL_CaptureMouse(want_capture);
626
80.5k
    }
627
628
80.5k
    SDL_Window* focused_window = SDL_GetKeyboardFocus();
629
80.5k
    const bool is_app_focused = (bd->Window == focused_window);
630
#else
631
    SDL_Window* focused_window = bd->Window;
632
    const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
633
#endif
634
80.5k
    if (is_app_focused)
635
80.5k
    {
636
        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
637
80.5k
        if (io.WantSetMousePos)
638
0
            SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
639
640
        // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured)
641
        // Note that SDL_GetGlobalMouseState() is in theory slow on X11, but this only runs on rather specific cases. If a problem we may provide a way to opt-out this feature.
642
80.5k
        SDL_Window* hovered_window = SDL_GetMouseFocus();
643
80.5k
        const bool is_relative_mouse_mode = SDL_GetWindowRelativeMouseMode(bd->Window);
644
80.5k
        if (hovered_window == NULL && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode)
645
1.71k
        {
646
            // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
647
1.71k
            float mouse_x, mouse_y;
648
1.71k
            int window_x, window_y;
649
1.71k
            SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
650
1.71k
            SDL_GetWindowPosition(focused_window, &window_x, &window_y);
651
1.71k
            mouse_x -= window_x;
652
1.71k
            mouse_y -= window_y;
653
1.71k
            io.AddMousePosEvent(mouse_x, mouse_y);
654
1.71k
        }
655
80.5k
    }
656
80.5k
}
657
658
static void ImGui_ImplSDL3_UpdateMouseCursor()
659
80.5k
{
660
80.5k
    ImGuiIO& io = ImGui::GetIO();
661
80.5k
    if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
662
0
        return;
663
80.5k
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
664
665
80.5k
    ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
666
80.5k
    if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
667
0
    {
668
        // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
669
0
        SDL_HideCursor();
670
0
    }
671
80.5k
    else
672
80.5k
    {
673
        // Show OS mouse cursor
674
80.5k
        SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
675
80.5k
        if (bd->MouseLastCursor != expected_cursor)
676
1
        {
677
1
            SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
678
1
            bd->MouseLastCursor = expected_cursor;
679
1
        }
680
80.5k
        SDL_ShowCursor();
681
80.5k
    }
682
80.5k
}
683
684
static void ImGui_ImplSDL3_CloseGamepads()
685
2
{
686
2
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
687
2
    if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
688
2
        for (SDL_Gamepad* gamepad : bd->Gamepads)
689
0
            SDL_CloseGamepad(gamepad);
690
2
    bd->Gamepads.resize(0);
691
2
}
692
693
void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count)
694
0
{
695
0
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
696
0
    ImGui_ImplSDL3_CloseGamepads();
697
0
    if (mode == ImGui_ImplSDL3_GamepadMode_Manual)
698
0
    {
699
0
        IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0);
700
0
        for (int n = 0; n < manual_gamepads_count; n++)
701
0
            bd->Gamepads.push_back(manual_gamepads_array[n]);
702
0
    }
703
0
    else
704
0
    {
705
0
        IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
706
0
        bd->WantUpdateGamepadsList = true;
707
0
    }
708
0
    bd->GamepadMode = mode;
709
0
}
710
711
static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no)
712
0
{
713
0
    bool merged_value = false;
714
0
    for (SDL_Gamepad* gamepad : bd->Gamepads)
715
0
        merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0;
716
0
    io.AddKeyEvent(key, merged_value);
717
0
}
718
719
0
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v  > 1.0f ? 1.0f : v; }
720
static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1)
721
0
{
722
0
    float merged_value = 0.0f;
723
0
    for (SDL_Gamepad* gamepad : bd->Gamepads)
724
0
    {
725
0
        float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
726
0
        if (merged_value < vn)
727
0
            merged_value = vn;
728
0
    }
729
0
    io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
730
0
}
731
732
static void ImGui_ImplSDL3_UpdateGamepads()
733
80.5k
{
734
80.5k
    ImGuiIO& io = ImGui::GetIO();
735
80.5k
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
736
737
    // Update list of gamepads to use
738
80.5k
    if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
739
1
    {
740
1
        ImGui_ImplSDL3_CloseGamepads();
741
1
        int sdl_gamepads_count = 0;
742
1
        SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
743
1
        for (int n = 0; n < sdl_gamepads_count; n++)
744
0
            if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n]))
745
0
            {
746
0
                bd->Gamepads.push_back(gamepad);
747
0
                if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst)
748
0
                    break;
749
0
            }
750
1
        bd->WantUpdateGamepadsList = false;
751
1
        SDL_free(sdl_gamepads);
752
1
    }
753
754
80.5k
    io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
755
80.5k
    if (bd->Gamepads.Size == 0)
756
80.5k
        return;
757
0
    io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
758
759
    // Update gamepad inputs
760
0
    const int thumb_dead_zone = 8000;           // SDL_gamepad.h suggests using this value.
761
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart,       SDL_GAMEPAD_BUTTON_START);
762
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack,        SDL_GAMEPAD_BUTTON_BACK);
763
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft,    SDL_GAMEPAD_BUTTON_WEST);           // Xbox X, PS Square
764
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight,   SDL_GAMEPAD_BUTTON_EAST);           // Xbox B, PS Circle
765
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp,      SDL_GAMEPAD_BUTTON_NORTH);          // Xbox Y, PS Triangle
766
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown,    SDL_GAMEPAD_BUTTON_SOUTH);          // Xbox A, PS Cross
767
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft,    SDL_GAMEPAD_BUTTON_DPAD_LEFT);
768
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight,   SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
769
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp,      SDL_GAMEPAD_BUTTON_DPAD_UP);
770
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown,    SDL_GAMEPAD_BUTTON_DPAD_DOWN);
771
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1,          SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
772
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1,          SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
773
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2,          SDL_GAMEPAD_AXIS_LEFT_TRIGGER,  0.0f, 32767);
774
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2,          SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
775
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3,          SDL_GAMEPAD_BUTTON_LEFT_STICK);
776
0
    ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3,          SDL_GAMEPAD_BUTTON_RIGHT_STICK);
777
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft,  SDL_GAMEPAD_AXIS_LEFTX,  -thumb_dead_zone, -32768);
778
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX,  +thumb_dead_zone, +32767);
779
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp,    SDL_GAMEPAD_AXIS_LEFTY,  -thumb_dead_zone, -32768);
780
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown,  SDL_GAMEPAD_AXIS_LEFTY,  +thumb_dead_zone, +32767);
781
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft,  SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768);
782
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767);
783
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp,    SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768);
784
0
    ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown,  SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767);
785
0
}
786
787
static void ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(SDL_Window* window, ImVec2* out_size, ImVec2* out_framebuffer_scale)
788
80.5k
{
789
80.5k
    int w, h;
790
80.5k
    SDL_GetWindowSize(window, &w, &h);
791
80.5k
    if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
792
0
        w = h = 0;
793
794
80.5k
#if defined(__APPLE__)
795
80.5k
    float fb_scale_x = SDL_GetWindowDisplayScale(window); // Seems more reliable during resolution change (#8703)
796
80.5k
    float fb_scale_y = fb_scale_x;
797
#else
798
    int display_w, display_h;
799
    SDL_GetWindowSizeInPixels(window, &display_w, &display_h);
800
    float fb_scale_x = (w > 0) ? (float)display_w / w : 1.0f;
801
    float fb_scale_y = (h > 0) ? (float)display_h / h : 1.0f;
802
#endif
803
804
80.5k
    if (out_size != nullptr)
805
80.5k
        *out_size = ImVec2((float)w, (float)h);
806
80.5k
    if (out_framebuffer_scale != nullptr)
807
80.5k
        *out_framebuffer_scale = ImVec2(fb_scale_x, fb_scale_y);
808
80.5k
}
809
810
void ImGui_ImplSDL3_NewFrame()
811
80.5k
{
812
80.5k
    ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
813
80.5k
    IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
814
80.5k
    ImGuiIO& io = ImGui::GetIO();
815
816
    // Setup main viewport size (every frame to accommodate for window resizing)
817
80.5k
    ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale);
818
819
    // Setup time step (we could also use SDL_GetTicksNS() available since SDL3)
820
    // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
821
80.5k
    static Uint64 frequency = SDL_GetPerformanceFrequency();
822
80.5k
    Uint64 current_time = SDL_GetPerformanceCounter();
823
80.5k
    if (current_time <= bd->Time)
824
0
        current_time = bd->Time + 1;
825
80.5k
    io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
826
80.5k
    bd->Time = current_time;
827
828
80.5k
    if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
829
2
    {
830
2
        bd->MouseWindowID = 0;
831
2
        bd->MousePendingLeaveFrame = 0;
832
2
        io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
833
2
    }
834
835
80.5k
    ImGui_ImplSDL3_UpdateMouseData();
836
80.5k
    ImGui_ImplSDL3_UpdateMouseCursor();
837
838
    // Update game controllers (if enabled and available)
839
80.5k
    ImGui_ImplSDL3_UpdateGamepads();
840
80.5k
}
841
842
//-----------------------------------------------------------------------------
843
844
#if defined(__clang__)
845
#pragma clang diagnostic pop
846
#endif
847
848
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/backends/imgui_impl_sdlrenderer3.cpp
Line
Count
Source
1
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
2
// (Requires: SDL 3.1.8+)
3
4
// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete.
5
// For a multi-platform app consider using other technologies:
6
// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API.
7
// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers.
8
// If your application wants to render any non trivial amount of graphics other than UI,
9
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user
10
// and it might be difficult to step out of those boundaries.
11
12
// Implemented features:
13
//  [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
14
//  [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
15
//  [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
16
//  [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
17
18
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
19
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
20
// Learn about Dear ImGui:
21
// - FAQ                  https://dearimgui.com/faq
22
// - Getting Started      https://dearimgui.com/getting-started
23
// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).
24
// - Introduction, links and more at the top of imgui.cpp
25
26
// CHANGELOG
27
//  2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
28
//  2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer3_CreateFontsTexture() and ImGui_ImplSDLRenderer3_DestroyFontsTexture().
29
//  2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color.
30
//  2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
31
//  2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009).
32
//  2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
33
//  2024-02-12: Amend to query SDL_RenderViewportSet() and restore viewport accordingly.
34
//  2023-05-30: Initial version.
35
36
#include "imgui.h"
37
#ifndef IMGUI_DISABLE
38
#include "imgui_impl_sdlrenderer3.h"
39
#include <stdint.h>     // intptr_t
40
41
// Clang warnings with -Weverything
42
#if defined(__clang__)
43
#pragma clang diagnostic push
44
#pragma clang diagnostic ignored "-Wsign-conversion"    // warning: implicit conversion changes signedness
45
#elif defined(__GNUC__)
46
#pragma GCC diagnostic ignored "-Wfloat-equal"          // warning: comparing floating-point with '==' or '!=' is unsafe
47
#endif
48
49
// SDL
50
#include <SDL3/SDL.h>
51
#if !SDL_VERSION_ATLEAST(3,0,0)
52
#error This backend requires SDL 3.0.0+
53
#endif
54
55
// SDL_Renderer data
56
struct ImGui_ImplSDLRenderer3_Data
57
{
58
    SDL_Renderer*           Renderer;       // Main viewport's renderer
59
    ImVector<SDL_FColor>    ColorBuffer;
60
61
1
    ImGui_ImplSDLRenderer3_Data()   { memset((void*)this, 0, sizeof(*this)); }
62
};
63
64
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
65
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
66
static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData()
67
161k
{
68
161k
    return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
69
161k
}
70
71
// Functions
72
bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer)
73
1
{
74
1
    ImGuiIO& io = ImGui::GetIO();
75
1
    IMGUI_CHECKVERSION();
76
1
    IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
77
1
    IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!");
78
79
    // Setup backend capabilities flags
80
1
    ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)();
81
1
    io.BackendRendererUserData = (void*)bd;
82
1
    io.BackendRendererName = "imgui_impl_sdlrenderer3";
83
1
    io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
84
1
    io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;   // We can honor ImGuiPlatformIO::Textures[] requests during render.
85
86
1
    bd->Renderer = renderer;
87
88
1
    return true;
89
1
}
90
91
void ImGui_ImplSDLRenderer3_Shutdown()
92
1
{
93
1
    ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
94
1
    IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
95
1
    ImGuiIO& io = ImGui::GetIO();
96
1
    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
97
98
1
    ImGui_ImplSDLRenderer3_DestroyDeviceObjects();
99
100
1
    io.BackendRendererName = nullptr;
101
1
    io.BackendRendererUserData = nullptr;
102
1
    io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
103
1
    platform_io.ClearRendererHandlers();
104
1
    IM_DELETE(bd);
105
1
}
106
107
static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer)
108
80.5k
{
109
    // Clear out any viewports and cliprect set by the user
110
    // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
111
80.5k
    SDL_SetRenderViewport(renderer, nullptr);
112
80.5k
    SDL_SetRenderClipRect(renderer, nullptr);
113
80.5k
}
114
115
void ImGui_ImplSDLRenderer3_NewFrame()
116
80.5k
{
117
80.5k
    ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
118
80.5k
    IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?");
119
80.5k
    IM_UNUSED(bd);
120
80.5k
}
121
122
// https://github.com/libsdl-org/SDL/issues/9009
123
static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector<SDL_FColor>& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices)
124
162k
{
125
162k
    const Uint8* color2 = (const Uint8*)color;
126
162k
    colors_out.resize(num_vertices);
127
162k
    SDL_FColor* color3 = colors_out.Data;
128
11.9M
    for (int i = 0; i < num_vertices; i++)
129
11.7M
    {
130
11.7M
        color3[i].r = color->r / 255.0f;
131
11.7M
        color3[i].g = color->g / 255.0f;
132
11.7M
        color3[i].b = color->b / 255.0f;
133
11.7M
        color3[i].a = color->a / 255.0f;
134
11.7M
        color2 += color_stride;
135
11.7M
        color = (const SDL_Color*)color2;
136
11.7M
    }
137
162k
    return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices);
138
162k
}
139
140
void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer)
141
80.5k
{
142
80.5k
    ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
143
144
    // If there's a scale factor set by the user, use that instead
145
    // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
146
    // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
147
80.5k
    float rsx = 1.0f;
148
80.5k
    float rsy = 1.0f;
149
80.5k
    SDL_GetRenderScale(renderer, &rsx, &rsy);
150
80.5k
    ImVec2 render_scale;
151
80.5k
    render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;
152
80.5k
    render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;
153
154
    // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
155
80.5k
    int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);
156
80.5k
    int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);
157
80.5k
    if (fb_width == 0 || fb_height == 0)
158
0
        return;
159
160
    // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
161
    // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
162
80.5k
    if (draw_data->Textures != nullptr)
163
80.5k
        for (ImTextureData* tex : *draw_data->Textures)
164
80.5k
            if (tex->Status != ImTextureStatus_OK)
165
2
                ImGui_ImplSDLRenderer3_UpdateTexture(tex);
166
167
    // Backup SDL_Renderer state that will be modified to restore it afterwards
168
80.5k
    struct BackupSDLRendererState
169
80.5k
    {
170
80.5k
        SDL_Rect    Viewport;
171
80.5k
        bool        ViewportEnabled;
172
80.5k
        bool        ClipEnabled;
173
80.5k
        SDL_Rect    ClipRect;
174
80.5k
    };
175
80.5k
    BackupSDLRendererState old = {};
176
80.5k
    old.ViewportEnabled = SDL_RenderViewportSet(renderer);
177
80.5k
    old.ClipEnabled = SDL_RenderClipEnabled(renderer);
178
80.5k
    SDL_GetRenderViewport(renderer, &old.Viewport);
179
80.5k
    SDL_GetRenderClipRect(renderer, &old.ClipRect);
180
181
    // Setup desired state
182
80.5k
    ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
183
184
    // Setup render state structure (for callbacks and custom texture bindings)
185
80.5k
    ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
186
80.5k
    ImGui_ImplSDLRenderer3_RenderState render_state;
187
80.5k
    render_state.Renderer = renderer;
188
80.5k
    platform_io.Renderer_RenderState = &render_state;
189
190
    // Will project scissor/clipping rectangles into framebuffer space
191
80.5k
    ImVec2 clip_off = draw_data->DisplayPos;         // (0,0) unless using multi-viewports
192
80.5k
    ImVec2 clip_scale = render_scale;
193
194
    // Render command lists
195
80.5k
    for (const ImDrawList* draw_list : draw_data->CmdLists)
196
81.2k
    {
197
81.2k
        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data;
198
81.2k
        const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data;
199
200
243k
        for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
201
162k
        {
202
162k
            const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
203
162k
            if (pcmd->UserCallback)
204
0
            {
205
                // User callback, registered via ImDrawList::AddCallback()
206
                // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
207
0
                if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
208
0
                    ImGui_ImplSDLRenderer3_SetupRenderState(renderer);
209
0
                else
210
0
                    pcmd->UserCallback(draw_list, pcmd);
211
0
            }
212
162k
            else
213
162k
            {
214
                // Project scissor/clipping rectangles into framebuffer space
215
162k
                ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
216
162k
                ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
217
162k
                if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
218
162k
                if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
219
162k
                if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
220
162k
                if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
221
162k
                if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
222
0
                    continue;
223
224
162k
                SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };
225
162k
                SDL_SetRenderClipRect(renderer, &r);
226
227
162k
                const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos));
228
162k
                const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv));
229
162k
                const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+
230
231
                // Bind texture, Draw
232
162k
                SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();
233
162k
                SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex,
234
162k
                    xy, (int)sizeof(ImDrawVert),
235
162k
                    color, (int)sizeof(ImDrawVert),
236
162k
                    uv, (int)sizeof(ImDrawVert),
237
162k
                    draw_list->VtxBuffer.Size - pcmd->VtxOffset,
238
162k
                    idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));
239
162k
            }
240
162k
        }
241
81.2k
    }
242
80.5k
    platform_io.Renderer_RenderState = nullptr;
243
244
    // Restore modified SDL_Renderer state
245
80.5k
    SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr);
246
80.5k
    SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr);
247
80.5k
}
248
249
void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex)
250
3
{
251
3
    ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData();
252
253
3
    if (tex->Status == ImTextureStatus_WantCreate)
254
1
    {
255
        // Create and upload new texture to graphics system
256
        //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
257
1
        IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
258
1
        IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
259
260
        // Create texture
261
        // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
262
1
        SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height);
263
1
        IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!");
264
1
        SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch());
265
1
        SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND);
266
1
        SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_LINEAR);
267
268
        // Store identifiers
269
1
        tex->SetTexID((ImTextureID)(intptr_t)sdl_texture);
270
1
        tex->SetStatus(ImTextureStatus_OK);
271
1
    }
272
2
    else if (tex->Status == ImTextureStatus_WantUpdates)
273
1
    {
274
        // Update selected blocks. We only ever write to textures regions which have never been used before!
275
        // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
276
1
        SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID;
277
1
        for (ImTextureRect& r : tex->Updates)
278
4
        {
279
4
            SDL_Rect sdl_r = { r.x, r.y, r.w, r.h };
280
4
            SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch());
281
4
        }
282
1
        tex->SetStatus(ImTextureStatus_OK);
283
1
    }
284
1
    else if (tex->Status == ImTextureStatus_WantDestroy)
285
1
    {
286
1
        if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID)
287
1
            SDL_DestroyTexture(sdl_texture);
288
289
        // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
290
1
        tex->SetTexID(ImTextureID_Invalid);
291
1
        tex->SetStatus(ImTextureStatus_Destroyed);
292
1
    }
293
3
}
294
295
void ImGui_ImplSDLRenderer3_CreateDeviceObjects()
296
0
{
297
0
}
298
299
void ImGui_ImplSDLRenderer3_DestroyDeviceObjects()
300
1
{
301
    // Destroy all textures
302
1
    for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
303
1
        if (tex->RefCount == 1)
304
1
        {
305
1
            tex->SetStatus(ImTextureStatus_WantDestroy);
306
1
            ImGui_ImplSDLRenderer3_UpdateTexture(tex);
307
1
        }
308
1
}
309
310
//-----------------------------------------------------------------------------
311
312
#if defined(__clang__)
313
#pragma clang diagnostic pop
314
#endif
315
316
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui.cpp
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (main code and documentation)
3
4
// Help:
5
// - See links below.
6
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7
// - Read top of imgui.cpp for more details, links and comments.
8
9
// Resources:
10
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
11
// - Homepage ................... https://github.com/ocornut/imgui
12
// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
13
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
14
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
15
//   - Getting Started            https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
16
//   - Third-party Extensions     https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
17
//   - Bindings/Backends          https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
18
//   - Glossary                   https://github.com/ocornut/imgui/wiki/Glossary
19
//   - Debug Tools                https://github.com/ocornut/imgui/wiki/Debug-Tools
20
//   - Software using Dear ImGui  https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
21
// - Issues & support ........... https://github.com/ocornut/imgui/issues
22
// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
23
24
// For first-time users having issues compiling/linking/running:
25
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
26
// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
27
// Since 1.92, we encourage font loading questions to also be posted in 'Issues'.
28
29
// Copyright (c) 2014-2025 Omar Cornut
30
// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.
31
// See LICENSE.txt for copyright and licensing details (standard MIT License).
32
// This library is free but needs your support to sustain development and maintenance.
33
// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.
34
// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding
35
// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
36
37
// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.
38
// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without
39
// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't
40
// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you
41
// to a better solution or official support for them.
42
43
/*
44
45
Index of this file:
46
47
DOCUMENTATION
48
49
- MISSION STATEMENT
50
- CONTROLS GUIDE
51
- PROGRAMMER GUIDE
52
  - READ FIRST
53
  - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
54
  - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
55
  - HOW A SIMPLE APPLICATION MAY LOOK LIKE
56
  - USING CUSTOM BACKEND / CUSTOM ENGINE
57
- API BREAKING CHANGES (read me when you update!)
58
- FREQUENTLY ASKED QUESTIONS (FAQ)
59
  - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)
60
61
CODE
62
(search for "[SECTION]" in the code to find them)
63
64
// [SECTION] INCLUDES
65
// [SECTION] FORWARD DECLARATIONS
66
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
67
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
68
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
69
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
70
// [SECTION] MISC HELPERS/UTILITIES (File functions)
71
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
72
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
73
// [SECTION] ImGuiStorage
74
// [SECTION] ImGuiTextFilter
75
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
76
// [SECTION] ImGuiListClipper
77
// [SECTION] STYLING
78
// [SECTION] RENDER HELPERS
79
// [SECTION] INITIALIZATION, SHUTDOWN
80
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
81
// [SECTION] FONTS, TEXTURES
82
// [SECTION] ID STACK
83
// [SECTION] INPUTS
84
// [SECTION] ERROR CHECKING, STATE RECOVERY
85
// [SECTION] ITEM SUBMISSION
86
// [SECTION] LAYOUT
87
// [SECTION] SCROLLING
88
// [SECTION] TOOLTIPS
89
// [SECTION] POPUPS
90
// [SECTION] WINDOW FOCUS
91
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
92
// [SECTION] DRAG AND DROP
93
// [SECTION] LOGGING/CAPTURING
94
// [SECTION] SETTINGS
95
// [SECTION] LOCALIZATION
96
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
97
// [SECTION] PLATFORM DEPENDENT HELPERS
98
// [SECTION] METRICS/DEBUGGER WINDOW
99
// [SECTION] DEBUG LOG WINDOW
100
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
101
102
*/
103
104
//-----------------------------------------------------------------------------
105
// DOCUMENTATION
106
//-----------------------------------------------------------------------------
107
108
/*
109
110
 MISSION STATEMENT
111
 =================
112
113
 - Easy to use to create code-driven and data-driven tools.
114
 - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.
115
 - Easy to hack and improve.
116
 - Minimize setup and maintenance.
117
 - Minimize state storage on user side.
118
 - Minimize state synchronization.
119
 - Portable, minimize dependencies, run on target (consoles, phones, etc.).
120
 - Efficient runtime and memory consumption.
121
122
 Designed primarily for developers and content-creators, not the typical end-user!
123
 Some of the current weaknesses (which we aim to address in the future) includes:
124
125
 - Doesn't look fancy.
126
 - Limited layout features, intricate layouts are typically crafted in code.
127
128
129
 CONTROLS GUIDE
130
 ==============
131
132
 - MOUSE CONTROLS
133
   - Mouse wheel:                   Scroll vertically.
134
   - SHIFT+Mouse wheel:             Scroll horizontally.
135
   - Click [X]:                     Close a window, available when 'bool* p_open' is passed to ImGui::Begin().
136
   - Click ^, Double-Click title:   Collapse window.
137
   - Drag on corner/border:         Resize window (double-click to auto fit window to its contents).
138
   - Drag on any empty space:       Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).
139
   - Left-click outside popup:      Close popup stack (right-click over underlying popup: Partially close popup stack).
140
141
 - TEXT EDITOR
142
   - Hold SHIFT or Drag Mouse:      Select text.
143
   - CTRL+Left/Right:               Word jump.
144
   - CTRL+Shift+Left/Right:         Select words.
145
   - CTRL+A or Double-Click:        Select All.
146
   - CTRL+X, CTRL+C, CTRL+V:        Use OS clipboard.
147
   - CTRL+Z                         Undo.
148
   - CTRL+Y or CTRL+Shift+Z:        Redo.
149
   - ESCAPE:                        Revert text to its original value.
150
   - On OSX, controls are automatically adjusted to match standard OSX text editing 2ts and behaviors.
151
152
 - KEYBOARD CONTROLS
153
   - Basic:
154
     - Tab, SHIFT+Tab               Cycle through text editable fields.
155
     - CTRL+Tab, CTRL+Shift+Tab     Cycle through windows.
156
     - CTRL+Click                   Input text into a Slider or Drag widget.
157
   - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:
158
     - Tab, SHIFT+Tab:              Cycle through every items.
159
     - Arrow keys                   Move through items using directional navigation. Tweak value.
160
     - Arrow keys + Alt, Shift      Tweak slower, tweak faster (when using arrow keys).
161
     - Enter                        Activate item (prefer text input when possible).
162
     - Space                        Activate item (prefer tweaking with arrows when possible).
163
     - Escape                       Deactivate item, leave child window, close popup.
164
     - Page Up, Page Down           Previous page, next page.
165
     - Home, End                    Scroll to top, scroll to bottom.
166
     - Alt                          Toggle between scrolling layer and menu layer.
167
     - CTRL+Tab then Ctrl+Arrows    Move window. Hold SHIFT to resize instead of moving.
168
   - Output when ImGuiConfigFlags_NavEnableKeyboard set,
169
     - io.WantCaptureKeyboard flag is set when keyboard is claimed.
170
     - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.
171
     - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).
172
173
 - GAMEPAD CONTROLS
174
   - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
175
   - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!
176
   - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets
177
   - Backend support: backend needs to:
178
      - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.
179
      - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.
180
        Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).
181
   - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,
182
     with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.
183
184
 - REMOTE INPUTS SHARING & MOUSE EMULATION
185
   - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.
186
   - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)
187
     in order to share your PC mouse/keyboard.
188
   - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.
189
   - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the io.ConfigNavMoveSetMousePos flag.
190
     Enabling io.ConfigNavMoveSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.
191
     When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.
192
     When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.
193
     (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)
194
     (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want
195
     to set a boolean to ignore your other external mouse positions until the external source is moved again.)
196
197
198
 PROGRAMMER GUIDE
199
 ================
200
201
 READ FIRST
202
 ----------
203
 - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)
204
 - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!
205
   The UI can be highly dynamic, there are no construction or destruction steps, less superfluous
206
   data retention on your side, less state duplication, less state synchronization, fewer bugs.
207
 - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.
208
   Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.
209
 - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.
210
 - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).
211
   You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.
212
 - Dear ImGui is a "single pass" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.
213
   For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,
214
   where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches.
215
 - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.
216
 - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).
217
   If you get an assert, read the messages and comments around the assert.
218
 - This codebase aims to be highly optimized:
219
   - A typical idle frame should never call malloc/free.
220
   - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.
221
   - We put particular energy in making sure performances are decent with typical "Debug" build settings as well.
222
     Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all.
223
 - This codebase aims to be both highly opinionated and highly flexible:
224
   - This code works because of the things it choose to solve or not solve.
225
   - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,
226
     and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).
227
     This is to increase compatibility, increase maintainability and facilitate use from other languages.
228
   - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.
229
     See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that.
230
     We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.
231
   - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction
232
     (so don't use ImVector in your code or at our own risk!).
233
   - Building: We don't use nor mandate a build system for the main library.
234
     This is in an effort to ensure that it works in the real world aka with any esoteric build setup.
235
     This is also because providing a build system for the main library would be of little-value.
236
     The build problems are almost never coming from the main library but from specific backends.
237
238
239
 HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI
240
 ----------------------------------------------
241
 - Update submodule or copy/overwrite every file.
242
 - About imconfig.h:
243
   - You may modify your copy of imconfig.h, in this case don't overwrite it.
244
   - or you may locally branch to modify imconfig.h and merge/rebase latest.
245
   - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to
246
     specify a custom path for your imconfig.h file and instead not have to modify the default one.
247
248
 - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)
249
 - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master".
250
 - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file.
251
 - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes.
252
   If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed
253
   from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will
254
   likely be a comment about it. Please report any issue to the GitHub page!
255
 - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.
256
 - Try to keep your copy of Dear ImGui reasonably up to date!
257
258
259
 GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE
260
 ---------------------------------------------------------------
261
 - See https://github.com/ocornut/imgui/wiki/Getting-Started.
262
 - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.
263
 - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.
264
 - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.
265
   It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).
266
 - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.
267
 - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.
268
 - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.
269
   Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render"
270
   phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().
271
 - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.
272
 - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.
273
274
275
 HOW A SIMPLE APPLICATION MAY LOOK LIKE
276
 --------------------------------------
277
278
 USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).
279
 The sub-folders in examples/ contain examples applications following this structure.
280
281
     // Application init: create a dear imgui context, setup some options, load fonts
282
     ImGui::CreateContext();
283
     ImGuiIO& io = ImGui::GetIO();
284
     // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.
285
     // TODO: Fill optional fields of the io structure later.
286
     // TODO: Load TTF/OTF fonts if you don't want to use the default font.
287
288
     // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)
289
     ImGui_ImplWin32_Init(hwnd);
290
     ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
291
292
     // Application main loop
293
     while (true)
294
     {
295
         // Feed inputs to dear imgui, start new frame
296
         ImGui_ImplDX11_NewFrame();
297
         ImGui_ImplWin32_NewFrame();
298
         ImGui::NewFrame();
299
300
         // Any application code here
301
         ImGui::Text("Hello, world!");
302
303
         // Render dear imgui into framebuffer
304
         ImGui::Render();
305
         ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
306
         g_pSwapChain->Present(1, 0);
307
     }
308
309
     // Shutdown
310
     ImGui_ImplDX11_Shutdown();
311
     ImGui_ImplWin32_Shutdown();
312
     ImGui::DestroyContext();
313
314
 To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,
315
 you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
316
 Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this.
317
318
319
USING CUSTOM BACKEND / CUSTOM ENGINE
320
------------------------------------
321
322
IMPLEMENTING YOUR PLATFORM BACKEND:
323
 -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for basic instructions.
324
 -> the Platform backends in impl_impl_XXX.cpp files contain many implementations.
325
326
IMPLEMENTING YOUR RenderDrawData() function:
327
 -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md
328
 -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_RenderDrawData() function.
329
330
IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures:
331
 -> see https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md
332
 -> the Renderer Backends in impl_impl_XXX.cpp files contain many implementations of a ImGui_ImplXXXX_UpdateTexture() function.
333
334
 Basic application/backend skeleton:
335
336
     // Application init: create a Dear ImGui context, setup some options, load fonts
337
     ImGui::CreateContext();
338
     ImGuiIO& io = ImGui::GetIO();
339
     // TODO: set io.ConfigXXX values, e.g.
340
     io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable keyboard controls
341
342
     // TODO: Load TTF/OTF fonts if you don't want to use the default font.
343
     io.Fonts->AddFontFromFileTTF("NotoSans.ttf");
344
345
     // Application main loop
346
     while (true)
347
     {
348
        // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.
349
        // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)
350
        io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)
351
        io.DisplaySize.x = 1920.0f;             // set the current display width
352
        io.DisplaySize.y = 1280.0f;             // set the current display height here
353
        io.AddMousePosEvent(mouse_x, mouse_y);  // update mouse position
354
        io.AddMouseButtonEvent(0, mouse_b[0]);  // update mouse button states
355
        io.AddMouseButtonEvent(1, mouse_b[1]);  // update mouse button states
356
357
        // Call NewFrame(), after this point you can use ImGui::* functions anytime
358
        // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)
359
        ImGui::NewFrame();
360
361
        // Most of your application code here
362
        ImGui::Text("Hello, world!");
363
        MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End();
364
        MyGameRender(); // may use any Dear ImGui functions as well!
365
366
        // End the dear imgui frame
367
        // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)
368
        ImGui::EndFrame(); // this is automatically called by Render(), but available
369
        ImGui::Render();
370
371
        // Update textures
372
        ImDrawData* draw_data = ImGui::GetDrawData();
373
        for (ImTextureData* tex : *draw_data->Textures)
374
            if (tex->Status != ImTextureStatus_OK)
375
                MyImGuiBackend_UpdateTexture(tex);
376
377
        // Render dear imgui contents, swap buffers
378
        MyImGuiBackend_RenderDrawData(draw_data);
379
        SwapBuffers();
380
     }
381
382
     // Shutdown
383
     ImGui::DestroyContext();
384
385
386
387
 API BREAKING CHANGES
388
 ====================
389
390
 Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.
391
 Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.
392
 When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.
393
 You can read releases logs https://github.com/ocornut/imgui/releases for more details.
394
395
 - 2025/10/14 (1.92.4) - TreeNode, Selectable, Clipper: commented out legacy names which were obsoleted in 1.89.7 (July 2023) and 1.89.9 (Sept 2023);
396
                         - ImGuiTreeNodeFlags_AllowItemOverlap       --> ImGuiTreeNodeFlags_AllowOverlap
397
                         - ImGuiSelectableFlags_AllowItemOverlap     --> ImGuiSelectableFlags_AllowOverlap
398
                         - ImGuiListClipper::IncludeRangeByIndices() --> ImGuiListClipper::IncludeItemsByIndex()
399
 - 2025/08/08 (1.92.2) - Backends: SDL_GPU3: Changed ImTextureID type from SDL_GPUTextureSamplerBinding* to SDL_GPUTexture*, which is more natural and easier for user to manage. If you need to change the current sampler, you can access the ImGui_ImplSDLGPU3_RenderState struct. (#8866, #8163, #7998, #7988)
400
 - 2025/07/31 (1.92.2) - Tabs: Renamed ImGuiTabBarFlags_FittingPolicyResizeDown to ImGuiTabBarFlags_FittingPolicyShrink. Kept inline redirection enum (will obsolete).
401
 - 2025/06/25 (1.92.0) - Layout: commented out legacy ErrorCheckUsingSetCursorPosToExtendParentBoundaries() fallback obsoleted in 1.89 (August 2022) which allowed a SetCursorPos()/SetCursorScreenPos() call WITHOUT AN ITEM
402
                         to extend parent window/cell boundaries. Replaced with assert/tooltip that would already happens if previously using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#5548, #4510, #3355, #1760, #1490, #4152, #150)
403
                         - Incorrect way to make a window content size 200x200:
404
                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
405
                         - Correct ways to make a window content size 200x200:
406
                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
407
                              Begin(...) + Dummy(ImVec2(200,200)) + End();
408
                         - TL;DR; if the assert triggers, you can add a Dummy({0,0}) call to validate extending parent boundaries.
409
 - 2025/06/11 (1.92.0) - THIS VERSION CONTAINS THE LARGEST AMOUNT OF BREAKING CHANGES SINCE 2015! I TRIED REALLY HARD TO KEEP THEM TO A MINIMUM, REDUCE THE AMOUNT OF INTERFERENCES, BUT INEVITABLY SOME USERS WILL BE AFFECTED.
410
                         IN ORDER TO HELP US IMPROVE THE TRANSITION PROCESS, INCL. DOCUMENTATION AND COMMENTS, PLEASE REPORT **ANY** DOUBT, CONFUSION, QUESTIONS, FEEDBACK TO: https://github.com/ocornut/imgui/issues/
411
                         As part of the plan to reduce impact of API breaking changes, several unfinished changes/features/refactors related to font and text systems and scaling will be part of subsequent releases (1.92.1+).
412
                         If you are updating from an old version, and expecting a massive or difficult update, consider first updating to 1.91.9 to reduce the amount of changes.
413
                       - Hard to read? Refer to 'docs/Changelog.txt' for a less compact and more complete version of this!
414
                       - Fonts: **IMPORTANT**: if your app was solving the OSX/iOS Retina screen specific logical vs display scale problem by setting io.DisplayFramebufferScale (e.g. to 2.0f) + setting io.FontGlobalScale (e.g. to 1.0f/2.0f) + loading fonts at scaled sizes (e.g. size X * 2.0f):
415
                         This WILL NOT map correctly to the new system! Because font will rasterize as requested size.
416
                         - With a legacy backend (< 1.92): Instead of setting io.FontGlobalScale = 1.0f/N -> set ImFontCfg::RasterizerDensity = N. This already worked before, but is now pretty much required.
417
                         - With a new backend (1.92+): This should be all automatic. FramebufferScale is automatically used to set current font RasterizerDensity. FramebufferScale is a per-viewport property provided by backend through the Platform_GetWindowFramebufferScale() handler in 'docking' branch.
418
                       - Fonts: **IMPORTANT** on Font Sizing: Before 1.92, fonts were of a single size. They can now be dynamically sized.
419
                         - PushFont() API now has a REQUIRED size parameter.
420
                         - Before 1.92: PushFont() always used font "default" size specified in AddFont() call. It is equivalent to calling PushFont(font, font->LegacySize).
421
                         - Since  1.92: PushFont(font, 0.0f) preserve the current font size which is a shared value.
422
                         - To use old behavior: use 'ImGui::PushFont(font, font->LegacySize)' at call site.
423
                         - Kept inline single parameter function. Will obsolete.
424
                       - Fonts: **IMPORTANT** on Font Merging:
425
                         - When searching for a glyph in multiple merged fonts: we search for the FIRST font source which contains the desired glyph.
426
                           Because the user doesn't need to provide glyph ranges any more, it is possible that a glyph that you expected to fetch from a secondary/merged icon font may be erroneously fetched from the primary font.
427
                         - When searching for a glyph in multiple merged fonts: we now search for the FIRST font source which contains the desired glyph. This is technically a different behavior than before!
428
                         - e.g. If you are merging fonts you may have glyphs that you expected to load from Font Source 2 which exists in Font Source 1.
429
                           After the update and when using a new backend, those glyphs may now loaded from Font Source 1!
430
                         - We added `ImFontConfig::GlyphExcludeRanges[]` to specify ranges to exclude from a given font source:
431
                             // Add Font Source 1 but ignore ICON_MIN_FA..ICON_MAX_FA range
432
                             static ImWchar exclude_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
433
                             ImFontConfig cfg1;
434
                             cfg1.GlyphExcludeRanges = exclude_ranges;
435
                             io.Fonts->AddFontFromFileTTF("segoeui.ttf", 0.0f, &cfg1);
436
                             // Add Font Source 2, which expects to use the range above
437
                             ImFontConfig cfg2;
438
                             cfg2.MergeMode = true;
439
                             io.Fonts->AddFontFromFileTTF("FontAwesome4.ttf", 0.0f, &cfg2);
440
                         - You can use `Metrics/Debugger->Fonts->Font->Input Glyphs Overlap Detection Tool` to see list of glyphs available in multiple font sources. This can facilitate unde
441
                       - Fonts: **IMPORTANT** on Thread Safety:
442
                          - A few functions such as font->CalcTextSizeA() were, by sheer luck (== accidentally) thread-safe even thou we had never provided that guarantee. They are definitively not thread-safe anymore as new glyphs may be loaded.
443
                       - Fonts: ImFont::FontSize was removed and does not make sense anymore. ImFont::LegacySize is the size passed to AddFont().
444
                       - Fonts: Removed support for PushFont(NULL) which was a shortcut for "default font".
445
                       - Fonts: Renamed/moved 'io.FontGlobalScale' to 'style.FontScaleMain'.
446
                       - Textures: all API functions taking a 'ImTextureID' parameter are now taking a 'ImTextureRef'. Affected functions are: ImGui::Image(), ImGui::ImageWithBg(), ImGui::ImageButton(), ImDrawList::AddImage(), ImDrawList::AddImageQuad(), ImDrawList::AddImageRounded().
447
                       - Fonts: obsoleted ImFontAtlas::GetTexDataAsRGBA32(), GetTexDataAsAlpha8(), Build(), SetTexID(), IsBuilt() functions. The new protocol for backends to handle textures doesn't need them. Kept redirection functions (will obsolete).
448
                       - Fonts: ImFontConfig::OversampleH/OversampleV default to automatic (== 0) since v1.91.8. It is quite important you keep it automatic until we decide if we want to provide a way to express finer policy, otherwise you will likely waste texture space when using large glyphs. Note that the imgui_freetype backend doesn't use and does not need oversampling.
449
                       - Fonts: specifying glyph ranges is now unnecessary. The value of ImFontConfig::GlyphRanges[] is only useful for legacy backends. All GetGlyphRangesXXXX() functions are now marked obsolete: GetGlyphRangesDefault(), GetGlyphRangesGreek(), GetGlyphRangesKorean(), GetGlyphRangesJapanese(), GetGlyphRangesChineseSimplifiedCommon(), GetGlyphRangesChineseFull(), GetGlyphRangesCyrillic(), GetGlyphRangesThai(), GetGlyphRangesVietnamese().
450
                       - Fonts: removed ImFontAtlas::TexDesiredWidth to enforce a texture width. (#327)
451
                       - Fonts: if you create and manage ImFontAtlas instances yourself (instead of relying on ImGuiContext to create one), you'll need to call ImFontAtlasUpdateNewFrame() yourself. An assert will trigger if you don't.
452
                       - Fonts: obsolete ImGui::SetWindowFontScale() which is not useful anymore. Prefer using 'PushFont(NULL, style.FontSizeBase * factor)' or to manipulate other scaling factors.
453
                       - Fonts: obsoleted ImFont::Scale which is not useful anymore.
454
                       - Fonts: generally reworked Internals of ImFontAtlas and ImFont. While in theory a vast majority of users shouldn't be affected, some use cases or extensions might be. Among other things:
455
                          - ImDrawCmd::TextureId has been changed to ImDrawCmd::TexRef.
456
                          - ImFontAtlas::TexID has been changed to ImFontAtlas::TexRef.
457
                          - ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]
458
                          - ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourceCount.
459
                          - Each ImFont has a number of ImFontBaked instances corresponding to actively used sizes. ImFont::GetFontBaked(size) retrieves the one for a given size.
460
                          - Fields moved from ImFont to ImFontBaked: IndexAdvanceX[], Glyphs[], Ascent, Descent, FindGlyph(), FindGlyphNoFallback(), GetCharAdvance().
461
                          - Fields moved from ImFontAtlas to ImFontAtlas->Tex: ImFontAtlas::TexWidth => TexData->Width, ImFontAtlas::TexHeight => TexData->Height, ImFontAtlas::TexPixelsAlpha8/TexPixelsRGBA32 => TexData->GetPixels().
462
                          - Widget code may use ImGui::GetFontBaked() instead of ImGui::GetFont() to access font data for current font at current font size (and you may use font->GetFontBaked(size) to access it for any other size.)
463
                       - Fonts: (users of imgui_freetype): renamed ImFontAtlas::FontBuilderFlags to ImFontAtlas::FontLoaderFlags. Renamed ImFontConfig::FontBuilderFlags to ImFontConfig::FontLoaderFlags. Renamed ImGuiFreeTypeBuilderFlags to ImGuiFreeTypeLoaderFlags.
464
                         If you used runtime imgui_freetype selection rather than the default IMGUI_ENABLE_FREETYPE compile-time option: Renamed/reworked ImFontBuilderIO into ImFontLoader. Renamed ImGuiFreeType::GetBuilderForFreeType() to ImGuiFreeType::GetFontLoader().
465
                           - old:  io.Fonts->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()
466
                           - new:  io.Fonts->FontLoader = ImGuiFreeType::GetFontLoader()
467
                           - new:  io.Fonts->SetFontLoader(ImGuiFreeType::GetFontLoader()) to change dynamically at runtime [from 1.92.1]
468
                       - Fonts: (users of custom rectangles, see #8466): Renamed AddCustomRectRegular() to AddCustomRect(). Added GetCustomRect() as a replacement for GetCustomRectByIndex() + CalcCustomRectUV().
469
                           - The output type of GetCustomRect() is now ImFontAtlasRect, which include UV coordinates. X->x, Y->y, Width->w, Height->h.
470
                           - old:
471
                                const ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(custom_rect_id);
472
                                ImVec2 uv0, uv1;
473
                                atlas->GetCustomRectUV(r, &uv0, &uv1);
474
                                ImGui::Image(atlas->TexRef, ImVec2(r->w, r->h), uv0, uv1);
475
                           - new;
476
                                ImFontAtlasRect r;
477
                                atlas->GetCustomRect(custom_rect_id, &r);
478
                                ImGui::Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);
479
                           - We added a redirecting typedef but haven't attempted to magically redirect the field names, as this API is rarely used and the fix is simple.
480
                           - Obsoleted AddCustomRectFontGlyph() as the API does not make sense for scalable fonts. Kept existing function which uses the font "default size" (Sources[0]->LegacySize). Added a helper AddCustomRectFontGlyphForSize() which is immediately marked obsolete, but can facilitate transitioning old code.
481
                           - Prefer adding a font source (ImFontConfig) using a custom/procedural loader.
482
                       - DrawList: Renamed ImDrawList::PushTextureID()/PopTextureID() to PushTexture()/PopTexture().
483
                       - Backends: removed ImGui_ImplXXXX_CreateFontsTexture()/ImGui_ImplXXXX_DestroyFontsTexture() for all backends that had them. They should not be necessary any more.
484
 - 2025/05/23 (1.92.0) - Fonts: changed ImFont::CalcWordWrapPositionA() to ImFont::CalcWordWrapPosition()
485
                            - old:  const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, ....);
486
                            - new:  const char* ImFont::CalcWordWrapPosition (float size,  const char* text, ....);
487
                         The leading 'float scale' parameters was changed to 'float size'. This was necessary as 'scale' is assuming standard font size which is a concept we aim to eliminate in an upcoming update. Kept inline redirection function.
488
 - 2025/05/15 (1.92.0) - TreeNode: renamed ImGuiTreeNodeFlags_NavLeftJumpsBackHere to ImGuiTreeNodeFlags_NavLeftJumpsToParent for clarity. Kept inline redirection enum (will obsolete).
489
 - 2025/05/15 (1.92.0) - Commented out PushAllowKeyboardFocus()/PopAllowKeyboardFocus() which was obsoleted in 1.89.4. Use PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop)/PopItemFlag() instead. (#3092)
490
 - 2025/05/15 (1.92.0) - Commented out ImGuiListClipper::ForceDisplayRangeByIndices() which was obsoleted in 1.89.6. Use ImGuiListClipper::IncludeItemsByIndex() instead.
491
 - 2025/03/05 (1.91.9) - BeginMenu(): Internals: reworked mangling of menu windows to use "###Menu_00" etc. instead of "##Menu_00", allowing them to also store the menu name before it. This shouldn't affect code unless directly accessing menu window from their mangled name.
492
 - 2025/04/16 (1.91.9) - Internals: RenderTextEllipsis() function removed the 'float clip_max_x' parameter directly preceding 'float ellipsis_max_x'. Values were identical for a vast majority of users. (#8387)
493
 - 2025/02/27 (1.91.9) - Image(): removed 'tint_col' and 'border_col' parameter from Image() function. Added ImageWithBg() replacement. (#8131, #8238)
494
                            - old: void Image      (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 tint_col = (1,1,1,1), ImVec4 border_col = (0,0,0,0));
495
                            - new: void Image      (ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1));
496
                            - new: void ImageWithBg(ImTextureID tex_id, ImVec2 image_size, ImVec2 uv0 = (0,0), ImVec2 uv1 = (1,1), ImVec4 bg_col = (0,0,0,0), ImVec4 tint_col = (1,1,1,1));
497
                            - TL;DR: 'border_col' had misleading side-effect on layout, 'bg_col' was missing, parameter order couldn't be consistent with ImageButton().
498
                            - new behavior always use ImGuiCol_Border color + style.ImageBorderSize / ImGuiStyleVar_ImageBorderSize.
499
                            - old behavior altered border size (and therefore layout) based on border color's alpha, which caused variety of problems + old behavior a fixed 1.0f for border size which was not tweakable.
500
                            - kept legacy signature (will obsolete), which mimics the old behavior,  but uses Max(1.0f, style.ImageBorderSize) when border_col is specified.
501
                            - added ImageWithBg() function which has both 'bg_col' (which was missing) and 'tint_col'. It was impossible to add 'bg_col' to Image() with a parameter order consistent with other functions, so we decided to remove 'tint_col' and introduce ImageWithBg().
502
 - 2025/02/25 (1.91.9) - internals: fonts: ImFontAtlas::ConfigData[] has been renamed to ImFontAtlas::Sources[]. ImFont::ConfigData[], ConfigDataCount has been renamed to Sources[], SourcesCount.
503
 - 2025/02/06 (1.91.9) - renamed ImFontConfig::GlyphExtraSpacing.x to ImFontConfig::GlyphExtraAdvanceX.
504
 - 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior.
505
                         prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior.
506
                         the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls:
507
 - 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937)
508
 - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions.
509
                         As a result, old .ini data may be partially lost (docking and tables information particularly).
510
                         Because some users have crafted and storing .ini data as a way to workaround limitations of the docking API, we are providing a '#define IMGUI_USE_LEGACY_CRC32_ADLER' compile-time option to keep using old CRC32 tables if you cannot afford invalidating old .ini data.
511
 - 2024/11/06 (1.91.5) - commented/obsoleted out pre-1.87 IO system (equivalent to using IMGUI_DISABLE_OBSOLETE_KEYIO or IMGUI_DISABLE_OBSOLETE_FUNCTIONS before)
512
                            - io.KeyMap[] and io.KeysDown[] are removed (obsoleted February 2022).
513
                            - io.NavInputs[] and ImGuiNavInput are removed (obsoleted July 2022).
514
                            - pre-1.87 backends are not supported:
515
                               - backends need to call io.AddKeyEvent(), io.AddMouseEvent() instead of writing to io.KeysDown[], io.MouseDown[] fields.
516
                               - backends need to call io.AddKeyAnalogEvent() for gamepad values instead of writing to io.NavInputs[] fields.
517
                            - for more reference:
518
                              - read 1.87 and 1.88 part of this section or read Changelog for 1.87 and 1.88.
519
                              - read https://github.com/ocornut/imgui/issues/4921
520
                            - if you have trouble updating a very old codebase using legacy backend-specific key codes: consider updating to 1.91.4 first, then #define IMGUI_DISABLE_OBSOLETE_KEYIO, then update to latest.
521
                       - obsoleted ImGuiKey_COUNT (it is unusually error-prone/misleading since valid keys don't start at 0). probably use ImGuiKey_NamedKey_BEGIN/ImGuiKey_NamedKey_END?
522
                       - fonts: removed const qualifiers from most font functions in prevision for upcoming font improvements.
523
 - 2024/10/18 (1.91.4) - renamed ImGuiCol_NavHighlight to ImGuiCol_NavCursor (for consistency with newly exposed and reworked features). Kept inline redirection enum (will obsolete).
524
 - 2024/10/14 (1.91.4) - moved ImGuiConfigFlags_NavEnableSetMousePos to standalone io.ConfigNavMoveSetMousePos bool.
525
                         moved ImGuiConfigFlags_NavNoCaptureKeyboard to standalone io.ConfigNavCaptureKeyboard bool (note the inverted value!).
526
                         kept legacy names (will obsolete) + code that copies settings once the first time. Dynamically changing the old value won't work. Switch to using the new value!
527
 - 2024/10/10 (1.91.4) - the typedef for ImTextureID now defaults to ImU64 instead of void*. (#1641)
528
                         this removes the requirement to redefine it for backends which are e.g. storing descriptor sets or other 64-bits structures when building on 32-bits archs. It therefore simplify various building scripts/helpers.
529
                         you may have compile-time issues if you were casting to 'void*' instead of 'ImTextureID' when passing your types to functions taking ImTextureID values, e.g. ImGui::Image().
530
                         in doubt it is almost always better to do an intermediate intptr_t cast, since it allows casting any pointer/integer type without warning:
531
                            - May warn:    ImGui::Image((void*)MyTextureData, ...);
532
                            - May warn:    ImGui::Image((void*)(intptr_t)MyTextureData, ...);
533
                            - Won't warn:  ImGui::Image((ImTextureID)(intptr_t)MyTextureData, ...);
534
  -                      note that you can always define ImTextureID to be your own high-level structures (with dedicated constructors) if you like.
535
 - 2024/10/03 (1.91.3) - drags: treat v_min==v_max as a valid clamping range when != 0.0f. Zero is a still special value due to legacy reasons, unless using ImGuiSliderFlags_ClampZeroRange. (#7968, #3361, #76)
536
                       - drags: extended behavior of ImGuiSliderFlags_AlwaysClamp to include _ClampZeroRange. It considers v_min==v_max==0.0f as a valid clamping range (aka edits not allowed).
537
                         although unlikely, it you wish to only clamp on text input but want v_min==v_max==0.0f to mean unclamped drags, you can use _ClampOnInput instead of _AlwaysClamp. (#7968, #3361, #76)
538
 - 2024/09/10 (1.91.2) - internals: using multiple overlaid ButtonBehavior() with same ID will now have io.ConfigDebugHighlightIdConflicts=true feature emit a warning. (#8030)
539
                         it was one of the rare case where using same ID is legal. workarounds: (1) use single ButtonBehavior() call with multiple _MouseButton flags, or (2) surround the calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
540
 - 2024/08/23 (1.91.1) - renamed ImGuiChildFlags_Border to ImGuiChildFlags_Borders for consistency. kept inline redirection flag.
541
 - 2024/08/22 (1.91.1) - moved some functions from ImGuiIO to ImGuiPlatformIO structure:
542
                            - io.GetClipboardTextFn         -> platform_io.Platform_GetClipboardTextFn + changed 'void* user_data' to 'ImGuiContext* ctx'. Pull your user data from platform_io.ClipboardUserData.
543
                            - io.SetClipboardTextFn         -> platform_io.Platform_SetClipboardTextFn + same as above line.
544
                            - io.PlatformOpenInShellFn      -> platform_io.Platform_OpenInShellFn (#7660)
545
                            - io.PlatformSetImeDataFn       -> platform_io.Platform_SetImeDataFn
546
                            - io.PlatformLocaleDecimalPoint -> platform_io.Platform_LocaleDecimalPoint (#7389, #6719, #2278)
547
                            - access those via GetPlatformIO() instead of GetIO().
548
                         some were introduced very recently and often automatically setup by core library and backends, so for those we are exceptionally not maintaining a legacy redirection symbol.
549
                       - commented the old ImageButton() signature obsoleted in 1.89 (~August 2022). As a reminder:
550
                            - old ImageButton() before 1.89 used ImTextureId as item id (created issue with e.g. multiple buttons in same scope, transient texture id values, opaque computation of ID)
551
                            - new ImageButton() since 1.89 requires an explicit 'const char* str_id'
552
                            - old ImageButton() before 1.89 had frame_padding' override argument.
553
                            - new ImageButton() since 1.89 always use style.FramePadding, which you can freely override with PushStyleVar()/PopStyleVar().
554
 - 2024/07/25 (1.91.0) - obsoleted GetContentRegionMax(), GetWindowContentRegionMin() and GetWindowContentRegionMax(). (see #7838 on GitHub for more info)
555
                         you should never need those functions. you can do everything with GetCursorScreenPos() and GetContentRegionAvail() in a more simple way.
556
                            - instead of:  GetWindowContentRegionMax().x - GetCursorPos().x
557
                            - you can use: GetContentRegionAvail().x
558
                            - instead of:  GetWindowContentRegionMax().x + GetWindowPos().x
559
                            - you can use: GetCursorScreenPos().x + GetContentRegionAvail().x // when called from left edge of window
560
                            - instead of:  GetContentRegionMax()
561
                            - you can use: GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos() // right edge in local coordinates
562
                            - instead of:  GetWindowContentRegionMax().x - GetWindowContentRegionMin().x
563
                            - you can use: GetContentRegionAvail() // when called from left edge of window
564
 - 2024/07/15 (1.91.0) - renamed ImGuiSelectableFlags_DontClosePopups to ImGuiSelectableFlags_NoAutoClosePopups. (#1379, #1468, #2200, #4936, #5216, #7302, #7573)
565
                         (internals: also renamed ImGuiItemFlags_SelectableDontClosePopup into ImGuiItemFlags_AutoClosePopups with inverted behaviors)
566
 - 2024/07/15 (1.91.0) - obsoleted PushButtonRepeat()/PopButtonRepeat() in favor of using new PushItemFlag(ImGuiItemFlags_ButtonRepeat, ...)/PopItemFlag().
567
 - 2024/07/02 (1.91.0) - commented out obsolete ImGuiModFlags (renamed to ImGuiKeyChord in 1.89). (#4921, #456)
568
                       - commented out obsolete ImGuiModFlags_XXX values (renamed to ImGuiMod_XXX in 1.89). (#4921, #456)
569
                            - ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl, ImGuiModFlags_Shift -> ImGuiMod_Shift etc.
570
 - 2024/07/02 (1.91.0) - IO, IME: renamed platform IME hook and added explicit context for consistency and future-proofness.
571
                            - old: io.SetPlatformImeDataFn(ImGuiViewport* viewport, ImGuiPlatformImeData* data);
572
                            - new: io.PlatformSetImeDataFn(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
573
 - 2024/06/21 (1.90.9) - BeginChild: added ImGuiChildFlags_NavFlattened as a replacement for the window flag ImGuiWindowFlags_NavFlattened: the feature only ever made sense for BeginChild() anyhow.
574
                            - old: BeginChild("Name", size, 0, ImGuiWindowFlags_NavFlattened);
575
                            - new: BeginChild("Name", size, ImGuiChildFlags_NavFlattened, 0);
576
 - 2024/06/21 (1.90.9) - io: ClearInputKeys() (first exposed in 1.89.8) doesn't clear mouse data, newly added ClearInputMouse() does.
577
 - 2024/06/20 (1.90.9) - renamed ImGuiDragDropFlags_SourceAutoExpirePayload to ImGuiDragDropFlags_PayloadAutoExpire.
578
 - 2024/06/18 (1.90.9) - style: renamed ImGuiCol_TabActive -> ImGuiCol_TabSelected, ImGuiCol_TabUnfocused -> ImGuiCol_TabDimmed, ImGuiCol_TabUnfocusedActive -> ImGuiCol_TabDimmedSelected.
579
 - 2024/06/10 (1.90.9) - removed old nested structure: renaming ImGuiStorage::ImGuiStoragePair type to ImGuiStoragePair (simpler for many languages).
580
 - 2024/06/06 (1.90.8) - reordered ImGuiInputTextFlags values. This should not be breaking unless you are using generated headers that have values not matching the main library.
581
 - 2024/06/06 (1.90.8) - removed 'ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft', was mostly unused and misleading.
582
 - 2024/05/27 (1.90.7) - commented out obsolete symbols marked obsolete in 1.88 (May 2022):
583
                            - old: CaptureKeyboardFromApp(bool)
584
                            - new: SetNextFrameWantCaptureKeyboard(bool)
585
                            - old: CaptureMouseFromApp(bool)
586
                            - new: SetNextFrameWantCaptureMouse(bool)
587
 - 2024/05/22 (1.90.7) - inputs (internals): renamed ImGuiKeyOwner_None to ImGuiKeyOwner_NoOwner, to make use more explicit and reduce confusion with the default it is a non-zero value and cannot be the default value (never made public, but disclosing as I expect a few users caught on owner-aware inputs).
588
                       - inputs (internals): renamed ImGuiInputFlags_RouteGlobalLow -> ImGuiInputFlags_RouteGlobal, ImGuiInputFlags_RouteGlobal -> ImGuiInputFlags_RouteGlobalOverFocused, ImGuiInputFlags_RouteGlobalHigh -> ImGuiInputFlags_RouteGlobalHighest.
589
                       - inputs (internals): Shortcut(), SetShortcutRouting(): swapped last two parameters order in function signatures:
590
                            - old: Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);
591
                            - new: Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0, ImGuiID owner_id = 0);
592
                       - inputs (internals): owner-aware versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(): swapped last two parameters order in function signatures.
593
                            - old: IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
594
                            - new: IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);
595
                            - old: IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);
596
                            - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
597
                         for various reasons those changes makes sense. They are being made because making some of those API public.
598
                         only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL.
599
 - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys.
600
                           - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps.
601
                           - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456)
602
 - 2024/05/14 (1.90.7) - backends: SDL_Renderer2 and SDL_Renderer3 backend now take a SDL_Renderer* in their RenderDrawData() functions.
603
 - 2024/04/18 (1.90.6) - TreeNode: Fixed a layout inconsistency when using an empty/hidden label followed by a SameLine() call. (#7505, #282)
604
                           - old: TreeNode("##Hidden"); SameLine(); Text("Hello");     // <-- This was actually incorrect! BUT appeared to look ok with the default style where ItemSpacing.x == FramePadding.x * 2 (it didn't look aligned otherwise).
605
                           - new: TreeNode("##Hidden"); SameLine(0, 0); Text("Hello"); // <-- This is correct for all styles values.
606
                         with the fix, IF you were successfully using TreeNode("")+SameLine(); you will now have extra spacing between your TreeNode and the following item.
607
                         You'll need to change the SameLine() call to SameLine(0,0) to remove this extraneous spacing. This seemed like the more sensible fix that's not making things less consistent.
608
                         (Note: when using this idiom you are likely to also use ImGuiTreeNodeFlags_SpanAvailWidth).
609
 - 2024/03/18 (1.90.5) - merged the radius_x/radius_y parameters in ImDrawList::AddEllipse(), AddEllipseFilled() and PathEllipticalArcTo() into a single ImVec2 parameter. Exceptionally, because those functions were added in 1.90, we are not adding inline redirection functions. The transition is easy and should affect few users. (#2743, #7417)
610
 - 2024/03/08 (1.90.5) - inputs: more formally obsoleted GetKeyIndex() when IMGUI_DISABLE_OBSOLETE_FUNCTIONS is set. It has been unnecessary and a no-op since 1.87 (it returns the same value as passed when used with a 1.87+ backend using io.AddKeyEvent() function). (#4921)
611
                           - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX)
612
 - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
613
 - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter.
614
 - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges.
615
 - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls.
616
 - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80.
617
 - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).
618
 - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).
619
                         those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.
620
 - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.
621
                           - old: BeginChild("Name", size, true)
622
                           - new: BeginChild("Name", size, ImGuiChildFlags_Border)
623
                           - old: BeginChild("Name", size, false)
624
                           - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None)
625
                         **AMEND FROM THE FUTURE: from 1.91.1, 'ImGuiChildFlags_Border' is called 'ImGuiChildFlags_Borders'**
626
 - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.
627
                           - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);
628
                           - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);
629
 - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).
630
 - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631)
631
 - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).
632
                           - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)
633
                           - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
634
                           - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);
635
                           - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);
636
 - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:
637
                           - GetWindowContentRegionWidth()  -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.
638
                           - ImDrawCornerFlags_XXX          -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.
639
                       - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details
640
 - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!
641
 - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector<ImDrawList*>. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)
642
 - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).
643
 - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).
644
 - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.
645
 - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)
646
 - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.
647
 - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3.
648
 - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
649
                           - ListBoxHeader()  -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
650
                           - ListBoxFooter()  -> use EndListBox()
651
 - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin().
652
 - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().
653
 - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:
654
                           - ImGuiSliderFlags_ClampOnInput        -> use ImGuiSliderFlags_AlwaysClamp
655
                           - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite
656
                           - ImDrawList::AddBezierCurve()         -> use ImDrawList::AddBezierCubic()
657
                           - ImDrawList::PathBezierCurveTo()      -> use ImDrawList::PathBezierCubicCurveTo()
658
 - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).
659
 - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner.
660
 - 2023/02/15 (1.89.4) - moved the optional "courtesy maths operators" implementation from imgui_internal.h in imgui.h.
661
                         Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,
662
                         it has been frequently requested by people to use our own. We had an opt-in define which was
663
                         previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)
664
                           - OK:     #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui.h" / #include "imgui_internal.h"
665
                           - Error:  #include "imgui.h" / #define IMGUI_DEFINE_MATH_OPERATORS / #include "imgui_internal.h"
666
 - 2023/02/07 (1.89.3) - backends: renamed "imgui_impl_sdl.cpp" to "imgui_impl_sdl2.cpp" and "imgui_impl_sdl.h" to "imgui_impl_sdl2.h". (#6146) This is in prevision for the future release of SDL3.
667
 - 2022/10/26 (1.89)   - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.
668
 - 2022/10/12 (1.89)   - removed runtime patching of invalid "%f"/"%0.f" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details.
669
 - 2022/09/26 (1.89)   - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).
670
                           - ImGuiKey_ModCtrl  and ImGuiModFlags_Ctrl  -> ImGuiMod_Ctrl
671
                           - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift
672
                           - ImGuiKey_ModAlt   and ImGuiModFlags_Alt   -> ImGuiMod_Alt
673
                           - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super
674
                         the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.
675
                         the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.
676
                         exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.
677
 - 2022/09/20 (1.89)   - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.
678
                         this will require uses of legacy backend-dependent indices to be casted, e.g.
679
                            - with imgui_impl_glfw:  IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);
680
                            - with imgui_impl_win32: IsKeyPressed('A')        -> IsKeyPressed((ImGuiKey)'A')
681
                            - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!
682
 - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);
683
 - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):
684
                         - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
685
                         - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.
686
                         - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)
687
 - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.
688
                       this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.
689
                         - previously this would make the window content size ~200x200:
690
                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();
691
                         - instead, please submit an item:
692
                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();
693
                         - alternative:
694
                              Begin(...) + Dummy(ImVec2(200,200)) + End();
695
                         - content size is now only extended when submitting an item!
696
                         - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.
697
                         - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.
698
 - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).
699
                        - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.
700
                        - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
701
                          - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values.
702
                          - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.
703
                        - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));
704
                          - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.
705
                          - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.
706
 - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).
707
                        - Official backends from 1.87+                  -> no issue.
708
                        - Official backends from 1.60 to 1.86           -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!
709
                        - Custom backends not writing to io.NavInputs[] -> no issue.
710
                        - Custom backends writing to io.NavInputs[]     -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!
711
                        - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].
712
 - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).
713
 - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.
714
 - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.
715
 - 2022/01/20 (1.87) - inputs: reworded gamepad IO.
716
                        - Backend writing to io.NavInputs[]            -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.
717
 - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputting text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).
718
 - 2022/01/17 (1.87) - inputs: reworked mouse IO.
719
                        - Backend writing to io.MousePos               -> backend should call io.AddMousePosEvent()
720
                        - Backend writing to io.MouseDown[]            -> backend should call io.AddMouseButtonEvent()
721
                        - Backend writing to io.MouseWheel             -> backend should call io.AddMouseWheelEvent()
722
                        - Backend writing to io.MouseHoveredViewport   -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]
723
                       note: for all calls to IO new functions, the Dear ImGui context should be bound/current.
724
                       read https://github.com/ocornut/imgui/issues/4921 for details.
725
 - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(), ImGui::IsKeyDown(). Removed GetKeyIndex(), now unnecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.
726
                        - IsKeyPressed(MY_NATIVE_KEY_XXX)              -> use IsKeyPressed(ImGuiKey_XXX)
727
                        - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX))      -> use IsKeyPressed(ImGuiKey_XXX)
728
                        - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to still function with legacy key codes).
729
                        - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.*
730
                     - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert.
731
                     - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper.
732
 - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.
733
 - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.
734
 - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)
735
                        - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()
736
                        - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x
737
                        - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
738
                        - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect
739
                        - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex
740
 - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings
741
 - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.
742
 - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
743
 - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
744
                        - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
745
                        - ImFont::GlyphRangesBuilder  -> use ImFontGlyphRangesBuilder
746
 - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().
747
                        - if you are using official backends from the source tree: you have nothing to do.
748
                        - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().
749
 - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.
750
                        - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft
751
                        - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight
752
                        - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.
753
                       flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.
754
                       breaking: the default with rounding > 0.0f is now "round all corners" vs old implicit "round no corners":
755
                        - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)
756
                        - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)
757
                        - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)
758
                        - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.
759
                       this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.
760
                       the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify "round all corners" and we sometimes encouraged using them as shortcuts.
761
                       legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).
762
 - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):
763
                        - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()
764
 - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.
765
 - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() "bool closed" parameter to "ImDrawFlags flags". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.
766
 - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.
767
 - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.
768
 - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).
769
                     - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).
770
                     - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).
771
 - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.
772
                     - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.
773
                     - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.
774
 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):
775
                        - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().
776
                        - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg
777
                        - ImGuiInputTextCallback              -> use ImGuiTextEditCallback
778
                        - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData
779
 - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).
780
 - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!
781
 - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.
782
 - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures
783
 - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.
784
 - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):
785
                        - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend
786
                        - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)
787
                        - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
788
                        - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT
789
                        - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT
790
                      - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):
791
                        - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = "%.Xf" where X is your value for decimal_precision.
792
                        - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.
793
 - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).
794
 - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).
795
 - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.
796
 - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.
797
 - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.
798
 - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!
799
 - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().
800
                       replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).
801
                       worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:
802
                       - if you omitted the 'power' parameter (likely!), you are not affected.
803
                       - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.
804
                       - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.
805
                       see https://github.com/ocornut/imgui/issues/3361 for all details.
806
                       kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.
807
                       for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.
808
                     - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.
809
 - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.
810
 - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]
811
 - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.
812
 - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().
813
 - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.
814
 - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.
815
 - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.
816
 - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):
817
                       - ShowTestWindow()                    -> use ShowDemoWindow()
818
                       - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)
819
                       - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
820
                       - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f))
821
                       - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()
822
                       - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg
823
                       - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding
824
                       - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap
825
                       - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS
826
 - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.
827
 - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).
828
 - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.
829
 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.
830
 - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.
831
 - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):
832
                       - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed
833
                       - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)
834
                       - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()
835
                       - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)
836
                       - ImFont::Glyph                       -> use ImFontGlyph
837
 - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse "typematic" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.
838
                       if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.
839
                       The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).
840
                       If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.
841
 - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).
842
 - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).
843
 - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.
844
 - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have
845
                       overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.
846
                       This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.
847
                       Please reach out if you are affected.
848
 - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).
849
 - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).
850
 - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.
851
 - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).
852
 - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).
853
 - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).
854
 - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!
855
 - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).
856
 - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!
857
 - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).
858
 - 2018/12/20 (1.67) - made it illegal to call Begin("") with an empty string. This somehow half-worked before but had various undesirable side-effects.
859
 - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.
860
 - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.
861
 - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).
862
 - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.
863
                       If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.
864
 - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)
865
 - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.
866
                       NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.
867
                       Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.
868
 - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).
869
 - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).
870
 - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).
871
 - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature.
872
 - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.
873
 - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.
874
 - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).
875
 - 2018/06/08 (1.62) - examples: the imgui_impl_XXX files have been split to separate platform (Win32, GLFW, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan,  etc.).
876
                       old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.
877
                       when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.
878
                       in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.
879
 - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.
880
 - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.
881
 - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more.
882
                       If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.
883
                       To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.
884
                       If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them.
885
 - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format",
886
                       consistent with other functions. Kept redirection functions (will obsolete).
887
 - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.
888
 - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).
889
 - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.
890
 - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.
891
 - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.
892
 - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.
893
 - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.
894
 - 2018/02/07 (1.60) - reorganized context handling to be more explicit,
895
                       - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.
896
                       - removed Shutdown() function, as DestroyContext() serve this purpose.
897
                       - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.
898
                       - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.
899
                       - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.
900
 - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.
901
 - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).
902
 - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).
903
 - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.
904
 - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.
905
 - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).
906
 - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags
907
 - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.
908
 - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.
909
 - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).
910
 - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).
911
                     - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).
912
 - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).
913
 - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).
914
 - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.
915
 - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.
916
                       Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.
917
 - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.
918
 - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.
919
 - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.
920
 - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
921
 - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.
922
 - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.
923
 - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.
924
                       removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.
925
                         IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)
926
                         IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)
927
                         IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]
928
 - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
929
 - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
930
 - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).
931
 - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
932
 - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
933
 - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
934
                     - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
935
                     - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
936
 - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
937
 - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicitly to fix.
938
 - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.
939
 - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.
940
 - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).
941
 - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).
942
 - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
943
 - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.
944
                     - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options.
945
                     - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))'
946
 - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse
947
 - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.
948
 - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.
949
 - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild().
950
 - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.
951
 - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.
952
 - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal.
953
 - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.
954
                       If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.
955
                       This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color:
956
                       ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }
957
                       If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.
958
 - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().
959
 - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.
960
 - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).
961
 - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.
962
 - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337).
963
 - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)
964
 - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).
965
 - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.
966
 - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.
967
 - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.
968
 - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.
969
 - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.
970
                       GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.
971
                       GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!
972
 - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize
973
 - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.
974
 - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason
975
 - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
976
                       you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.
977
 - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
978
                       this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
979
                     - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
980
                     - the signature of the io.RenderDrawListsFn handler has changed!
981
                       old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)
982
                       new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).
983
                         parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'
984
                         ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.
985
                         ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.
986
                     - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.
987
                     - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!
988
                     - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!
989
 - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.
990
 - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).
991
 - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.
992
 - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence
993
 - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!
994
 - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).
995
 - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).
996
 - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.
997
 - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened.
998
 - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).
999
 - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.
1000
 - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API
1001
 - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.
1002
 - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.
1003
 - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.
1004
 - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing
1005
 - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.
1006
 - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)
1007
 - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.
1008
 - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.
1009
 - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.
1010
 - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior
1011
 - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()
1012
 - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)
1013
 - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.
1014
 - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.
1015
 - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.
1016
                       - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];
1017
                       - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);
1018
                       you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.
1019
 - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()
1020
 - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)
1021
 - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets
1022
 - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)
1023
 - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)
1024
 - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility
1025
 - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()
1026
 - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)
1027
 - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)
1028
 - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()
1029
 - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn
1030
 - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)
1031
 - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite
1032
 - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes
1033
1034
1035
 FREQUENTLY ASKED QUESTIONS (FAQ)
1036
 ================================
1037
1038
 Read all answers online:
1039
   https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)
1040
 Read all answers locally (with a text editor or ideally a Markdown viewer):
1041
   docs/FAQ.md
1042
 Some answers are copied down here to facilitate searching in code.
1043
1044
 Q&A: Basics
1045
 ===========
1046
1047
 Q: Where is the documentation?
1048
 A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.
1049
    - Run the examples/ applications and explore them.
1050
    - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.
1051
    - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.
1052
    - The demo covers most features of Dear ImGui, so you can read the code and see its output.
1053
    - See documentation and comments at the top of imgui.cpp + effectively imgui.h.
1054
    - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the
1055
      examples/ folder to explain how to integrate Dear ImGui with your own engine/application.
1056
    - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.
1057
    - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.
1058
    - Your programming IDE is your friend, find the type or function declaration to find comments
1059
      associated with it.
1060
1061
 Q: What is this library called?
1062
 Q: What is the difference between Dear ImGui and traditional UI toolkits?
1063
 Q: Which version should I get?
1064
 >> This library is called "Dear ImGui", please don't call it "ImGui" :)
1065
 >> See https://www.dearimgui.com/faq for details.
1066
1067
 Q&A: Integration
1068
 ================
1069
1070
 Q: How to get started?
1071
 A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.
1072
1073
 Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?
1074
 A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!
1075
 >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.
1076
1077
 Q. How can I enable keyboard or gamepad controls?
1078
 Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)
1079
 Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...
1080
 Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...
1081
 Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...
1082
 >> See https://www.dearimgui.com/faq
1083
1084
 Q&A: Usage
1085
 ----------
1086
1087
 Q: About the ID Stack system..
1088
   - Why is my widget not reacting when I click on it?
1089
   - How can I have widgets with an empty label?
1090
   - How can I have multiple widgets with the same label?
1091
   - How can I have multiple windows with the same label?
1092
 Q: How can I display an image? What is ImTextureID, how does it work?
1093
 Q: How can I use my own math types instead of ImVec2?
1094
 Q: How can I interact with standard C++ types (such as std::string and std::vector)?
1095
 Q: How can I display custom shapes? (using low-level ImDrawList API)
1096
 >> See https://www.dearimgui.com/faq
1097
1098
 Q&A: Fonts, Text
1099
 ================
1100
1101
 Q: How should I handle DPI in my application?
1102
 Q: How can I load a different font than the default?
1103
 Q: How can I easily use icons in my application?
1104
 Q: How can I load multiple fonts?
1105
 Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?
1106
 >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md
1107
1108
 Q&A: Concerns
1109
 =============
1110
1111
 Q: Who uses Dear ImGui?
1112
 Q: Can you create elaborate/serious tools with Dear ImGui?
1113
 Q: Can you reskin the look of Dear ImGui?
1114
 Q: Why using C++ (as opposed to C)?
1115
 >> See https://www.dearimgui.com/faq
1116
1117
 Q&A: Community
1118
 ==============
1119
1120
 Q: How can I help?
1121
 A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui!
1122
      We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.
1123
      This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.
1124
      >>> See https://github.com/ocornut/imgui/wiki/Funding
1125
    - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.
1126
    - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!
1127
    - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.
1128
      You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.
1129
      But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.
1130
    - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on GitHub or privately).
1131
1132
*/
1133
1134
//-------------------------------------------------------------------------
1135
// [SECTION] INCLUDES
1136
//-------------------------------------------------------------------------
1137
1138
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
1139
#define _CRT_SECURE_NO_WARNINGS
1140
#endif
1141
1142
#ifndef IMGUI_DEFINE_MATH_OPERATORS
1143
#define IMGUI_DEFINE_MATH_OPERATORS
1144
#endif
1145
1146
#include "imgui.h"
1147
#ifndef IMGUI_DISABLE
1148
#include "imgui_internal.h"
1149
1150
// System includes
1151
#include <stdio.h>      // vsnprintf, sscanf, printf
1152
#include <stdint.h>     // intptr_t
1153
1154
// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled
1155
#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
1156
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1157
#endif
1158
1159
// [Windows] OS specific includes (optional)
1160
#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && defined(IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1161
#define IMGUI_DISABLE_WIN32_FUNCTIONS
1162
#endif
1163
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
1164
#ifndef WIN32_LEAN_AND_MEAN
1165
#define WIN32_LEAN_AND_MEAN
1166
#endif
1167
#ifndef NOMINMAX
1168
#define NOMINMAX
1169
#endif
1170
#ifndef __MINGW32__
1171
#include <Windows.h>        // _wfopen, OpenClipboard
1172
#else
1173
#include <windows.h>
1174
#endif
1175
#if defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES))
1176
// The UWP and GDK Win32 API subsets don't support clipboard nor IME functions
1177
#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
1178
#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
1179
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
1180
#endif
1181
#endif
1182
1183
// [Apple] OS specific includes
1184
#if defined(__APPLE__)
1185
#include <TargetConditionals.h>
1186
#endif
1187
1188
// Visual Studio warnings
1189
#ifdef _MSC_VER
1190
#pragma warning (disable: 4127)             // condition expression is constant
1191
#pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
1192
#if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later
1193
#pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types
1194
#endif
1195
#pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
1196
#pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
1197
#pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
1198
#endif
1199
1200
// Clang/GCC warnings with -Weverything
1201
#if defined(__clang__)
1202
#if __has_warning("-Wunknown-warning-option")
1203
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
1204
#endif
1205
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
1206
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
1207
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
1208
#pragma clang diagnostic ignored "-Wformat"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'
1209
#pragma clang diagnostic ignored "-Wformat-nonliteral"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
1210
#pragma clang diagnostic ignored "-Wformat-pedantic"                // warning: format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.
1211
#pragma clang diagnostic ignored "-Wexit-time-destructors"          // warning: declaration requires an exit-time destructor     // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
1212
#pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
1213
#pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
1214
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type 'int'
1215
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
1216
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
1217
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
1218
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
1219
#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
1220
#pragma clang diagnostic ignored "-Wswitch-default"                 // warning: 'switch' missing 'default' label
1221
#elif defined(__GNUC__)
1222
// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.
1223
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
1224
#pragma GCC diagnostic ignored "-Wunused-function"                  // warning: 'xxxx' defined but not used
1225
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"              // warning: cast to pointer from integer of different size
1226
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
1227
#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
1228
#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
1229
#pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
1230
#pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
1231
#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false
1232
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
1233
#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
1234
#endif
1235
1236
// Debug options
1237
161k
#define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Hold CTRL to display for all candidates. CTRL+Arrow to change last direction.
1238
#define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window
1239
1240
// Default font size if unspecified in both style.FontSizeBase and AddFontXXX() calls.
1241
static const float FONT_DEFAULT_SIZE = 20.0f;
1242
1243
// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.
1244
static const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in
1245
static const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear
1246
static const float NAV_ACTIVATE_HIGHLIGHT_TIMER             = 0.10f;    // Time to highlight an item activated by a shortcut.
1247
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.
1248
static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 0.70f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
1249
1250
// Tooltip offset
1251
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10);      // Multiplied by g.Style.MouseCursorScale
1252
static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20);      // Multiplied by g.Style.MouseCursorScale
1253
static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f);   // Multiplied by g.Style.MouseCursorScale
1254
1255
//-------------------------------------------------------------------------
1256
// [SECTION] FORWARD DECLARATIONS
1257
//-------------------------------------------------------------------------
1258
1259
static void             SetCurrentWindow(ImGuiWindow* window);
1260
static ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);
1261
static ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);
1262
1263
static void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);
1264
1265
// Settings
1266
static void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);
1267
static void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);
1268
static void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);
1269
static void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);
1270
static void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);
1271
1272
// Platform Dependents default implementation for ImGuiPlatformIO functions
1273
static const char*      Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx);
1274
static void             Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text);
1275
static void             Platform_SetImeDataFn_DefaultImpl(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
1276
static bool             Platform_OpenInShellFn_DefaultImpl(ImGuiContext* ctx, const char* path);
1277
1278
namespace ImGui
1279
{
1280
// Item
1281
static void             ItemHandleShortcut(ImGuiID id);
1282
1283
// Window Focus
1284
static int              FindWindowFocusIndex(ImGuiWindow* window);
1285
static void             UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags);
1286
1287
// Navigation
1288
static void             NavUpdate();
1289
static void             NavUpdateWindowing();
1290
static void             NavUpdateWindowingApplyFocus(ImGuiWindow* window);
1291
static void             NavUpdateWindowingOverlay();
1292
static void             NavUpdateCancelRequest();
1293
static void             NavUpdateCreateMoveRequest();
1294
static void             NavUpdateCreateTabbingRequest();
1295
static float            NavUpdatePageUpPageDown();
1296
static inline void      NavUpdateAnyRequestFlag();
1297
static void             NavUpdateCreateWrappingRequest();
1298
static void             NavEndFrame();
1299
static bool             NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb);
1300
static void             NavApplyItemToResult(ImGuiNavItemData* result);
1301
static void             NavProcessItem();
1302
static void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);
1303
static ImGuiInputSource NavCalcPreferredRefPosSource();
1304
static ImVec2           NavCalcPreferredRefPos();
1305
static void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);
1306
static ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);
1307
static void             NavRestoreLayer(ImGuiNavLayer layer);
1308
1309
// Error Checking and Debug Tools
1310
static void             ErrorCheckNewFrameSanityChecks();
1311
static void             ErrorCheckEndFrameSanityChecks();
1312
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1313
static void             UpdateDebugToolItemPicker();
1314
static void             UpdateDebugToolStackQueries();
1315
static void             UpdateDebugToolFlashStyleColor();
1316
#endif
1317
1318
// Inputs
1319
static void             UpdateKeyboardInputs();
1320
static void             UpdateMouseInputs();
1321
static void             UpdateMouseWheel();
1322
static void             UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);
1323
1324
// Misc
1325
static void             UpdateFontsNewFrame();
1326
static void             UpdateFontsEndFrame();
1327
static void             UpdateTexturesNewFrame();
1328
static void             UpdateTexturesEndFrame();
1329
static void             UpdateSettings();
1330
static int              UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);
1331
static void             RenderWindowOuterBorders(ImGuiWindow* window);
1332
static void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);
1333
static void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);
1334
static void             RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);
1335
static void             RenderDimmedBackgrounds();
1336
static void             SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect);
1337
static void             SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect);
1338
1339
// Viewports
1340
const ImGuiID           IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter.
1341
static void             UpdateViewportsNewFrame();
1342
1343
}
1344
1345
//-----------------------------------------------------------------------------
1346
// [SECTION] CONTEXT AND MEMORY ALLOCATORS
1347
//-----------------------------------------------------------------------------
1348
1349
// DLL users:
1350
// - Heaps and globals are not shared across DLL boundaries!
1351
// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.
1352
// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).
1353
// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
1354
// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).
1355
1356
// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.
1357
// - ImGui::CreateContext() will automatically set this pointer if it is NULL.
1358
//   Change to a different context by calling ImGui::SetCurrentContext().
1359
// - Important: Dear ImGui functions are not thread-safe because of this pointer.
1360
//   If you want thread-safety to allow N threads to access N different contexts:
1361
//   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:
1362
//         struct ImGuiContext;
1363
//         extern thread_local ImGuiContext* MyImGuiTLS;
1364
//         #define GImGui MyImGuiTLS
1365
//     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.
1366
//   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586
1367
//   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.
1368
// - DLL users: read comments above.
1369
#ifndef GImGui
1370
ImGuiContext*   GImGui = NULL;
1371
#endif
1372
1373
// Memory Allocator functions. Use SetAllocatorFunctions() to change them.
1374
// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.
1375
// - DLL users: read comments above.
1376
#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS
1377
243
static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }
1378
243
static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }
1379
#else
1380
static void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }
1381
static void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }
1382
#endif
1383
static ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;
1384
static ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;
1385
static void*                GImAllocatorUserData = NULL;
1386
1387
//-----------------------------------------------------------------------------
1388
// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO, ImGuiPlatformIO)
1389
//-----------------------------------------------------------------------------
1390
1391
ImGuiStyle::ImGuiStyle()
1392
1
{
1393
1
    FontSizeBase                = 0.0f;             // Will default to io.Fonts->Fonts[0] on first frame.
1394
1
    FontScaleMain               = 1.0f;             // Main scale factor. May be set by application once, or exposed to end-user.
1395
1
    FontScaleDpi                = 1.0f;             // Additional scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.
1396
1397
1
    Alpha                       = 1.0f;             // Global alpha applies to everything in Dear ImGui.
1398
1
    DisabledAlpha               = 0.60f;            // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
1399
1
    WindowPadding               = ImVec2(8,8);      // Padding within a window
1400
1
    WindowRounding              = 0.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
1401
1
    WindowBorderSize            = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1402
1
    WindowBorderHoverPadding    = 4.0f;             // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders.
1403
1
    WindowMinSize               = ImVec2(32,32);    // Minimum window size
1404
1
    WindowTitleAlign            = ImVec2(0.0f,0.5f);// Alignment for title bar text
1405
1
    WindowMenuButtonPosition    = ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.
1406
1
    ChildRounding               = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows
1407
1
    ChildBorderSize             = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1408
1
    PopupRounding               = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows
1409
1
    PopupBorderSize             = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.
1410
1
    FramePadding                = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)
1411
1
    FrameRounding               = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).
1412
1
    FrameBorderSize             = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.
1413
1
    ItemSpacing                 = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines
1414
1
    ItemInnerSpacing            = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)
1415
1
    CellPadding                 = ImVec2(4,2);      // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
1416
1
    TouchExtraPadding           = ImVec2(0,0);      // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
1417
1
    IndentSpacing               = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
1418
1
    ColumnsMinSpacing           = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
1419
1
    ScrollbarSize               = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar
1420
1
    ScrollbarRounding           = 9.0f;             // Radius of grab corners rounding for scrollbar
1421
1
    ScrollbarPadding            = 2.0f;             // Padding of scrollbar grab within its frame (same for both axises)
1422
1
    GrabMinSize                 = 12.0f;            // Minimum width/height of a grab box for slider/scrollbar
1423
1
    GrabRounding                = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
1424
1
    LogSliderDeadzone           = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
1425
1
    ImageBorderSize             = 0.0f;             // Thickness of border around tabs.
1426
1
    TabRounding                 = 5.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
1427
1
    TabBorderSize               = 0.0f;             // Thickness of border around tabs.
1428
1
    TabMinWidthBase             = 1.0f;             // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
1429
1
    TabMinWidthShrink           = 80.0f;            // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.
1430
1
    TabCloseButtonMinWidthSelected   = -1.0f;       // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
1431
1
    TabCloseButtonMinWidthUnselected = 0.0f;        // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
1432
1
    TabBarBorderSize            = 1.0f;             // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
1433
1
    TabBarOverlineSize          = 1.0f;             // Thickness of tab-bar overline, which highlights the selected tab-bar.
1434
1
    TableAngledHeadersAngle     = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).
1435
1
    TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell
1436
1
    TreeLinesFlags              = ImGuiTreeNodeFlags_DrawLinesNone;
1437
1
    TreeLinesSize               = 1.0f;             // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
1438
1
    TreeLinesRounding           = 0.0f;             // Radius of lines connecting child nodes to the vertical line.
1439
1
    ColorButtonPosition         = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
1440
1
    ButtonTextAlign             = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.
1441
1
    SelectableTextAlign         = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
1442
1
    SeparatorTextBorderSize     = 3.0f;             // Thickness of border in SeparatorText()
1443
1
    SeparatorTextAlign          = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
1444
1
    SeparatorTextPadding        = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
1445
1
    DisplayWindowPadding        = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.
1446
1
    DisplaySafeAreaPadding      = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.
1447
1
    MouseCursorScale            = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.
1448
1
    AntiAliasedLines            = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.
1449
1
    AntiAliasedLinesUseTex      = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
1450
1
    AntiAliasedFill             = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).
1451
1
    CurveTessellationTol        = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
1452
1
    CircleTessellationMaxError  = 0.30f;            // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
1453
1454
    // Behaviors
1455
1
    HoverStationaryDelay        = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
1456
1
    HoverDelayShort             = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
1457
1
    HoverDelayNormal            = 0.40f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
1458
1
    HoverFlagsForTooltipMouse   = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled;    // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
1459
1
    HoverFlagsForTooltipNav     = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
1460
1461
    // [Internal]
1462
1
    _MainScale                  = 1.0f;
1463
1
    _NextFrameFontSizeBase      = 0.0f;
1464
1465
    // Default theme
1466
1
    ImGui::StyleColorsDark(this);
1467
1
}
1468
1469
1470
// Scale all spacing/padding/thickness values. Do not scale fonts.
1471
// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.
1472
void ImGuiStyle::ScaleAllSizes(float scale_factor)
1473
0
{
1474
0
    _MainScale *= scale_factor;
1475
0
    WindowPadding = ImTrunc(WindowPadding * scale_factor);
1476
0
    WindowRounding = ImTrunc(WindowRounding * scale_factor);
1477
0
    WindowMinSize = ImTrunc(WindowMinSize * scale_factor);
1478
0
    WindowBorderHoverPadding = ImTrunc(WindowBorderHoverPadding * scale_factor);
1479
0
    ChildRounding = ImTrunc(ChildRounding * scale_factor);
1480
0
    PopupRounding = ImTrunc(PopupRounding * scale_factor);
1481
0
    FramePadding = ImTrunc(FramePadding * scale_factor);
1482
0
    FrameRounding = ImTrunc(FrameRounding * scale_factor);
1483
0
    ItemSpacing = ImTrunc(ItemSpacing * scale_factor);
1484
0
    ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);
1485
0
    CellPadding = ImTrunc(CellPadding * scale_factor);
1486
0
    TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);
1487
0
    IndentSpacing = ImTrunc(IndentSpacing * scale_factor);
1488
0
    ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);
1489
0
    ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);
1490
0
    ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);
1491
0
    ScrollbarPadding = ImTrunc(ScrollbarPadding * scale_factor);
1492
0
    GrabMinSize = ImTrunc(GrabMinSize * scale_factor);
1493
0
    GrabRounding = ImTrunc(GrabRounding * scale_factor);
1494
0
    LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);
1495
0
    ImageBorderSize = ImTrunc(ImageBorderSize * scale_factor);
1496
0
    TabRounding = ImTrunc(TabRounding * scale_factor);
1497
0
    TabMinWidthBase = ImTrunc(TabMinWidthBase * scale_factor);
1498
0
    TabMinWidthShrink = ImTrunc(TabMinWidthShrink * scale_factor);
1499
0
    TabCloseButtonMinWidthSelected = (TabCloseButtonMinWidthSelected > 0.0f && TabCloseButtonMinWidthSelected != FLT_MAX) ? ImTrunc(TabCloseButtonMinWidthSelected * scale_factor) : TabCloseButtonMinWidthSelected;
1500
0
    TabCloseButtonMinWidthUnselected = (TabCloseButtonMinWidthUnselected > 0.0f && TabCloseButtonMinWidthUnselected != FLT_MAX) ? ImTrunc(TabCloseButtonMinWidthUnselected * scale_factor) : TabCloseButtonMinWidthUnselected;
1501
0
    TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor);
1502
0
    TreeLinesRounding = ImTrunc(TreeLinesRounding * scale_factor);
1503
0
    SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);
1504
0
    DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);
1505
0
    DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);
1506
0
    MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);
1507
0
}
1508
1509
ImGuiIO::ImGuiIO()
1510
1
{
1511
    // Most fields are initialized with zero
1512
1
    memset(this, 0, sizeof(*this));
1513
1
    IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);
1514
1515
    // Settings
1516
1
    ConfigFlags = ImGuiConfigFlags_None;
1517
1
    BackendFlags = ImGuiBackendFlags_None;
1518
1
    DisplaySize = ImVec2(-1.0f, -1.0f);
1519
1
    DeltaTime = 1.0f / 60.0f;
1520
1
    IniSavingRate = 5.0f;
1521
1
    IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
1522
1
    LogFilename = "imgui_log.txt";
1523
1
    UserData = NULL;
1524
1525
1
    Fonts = NULL;
1526
1
    FontDefault = NULL;
1527
1
    FontAllowUserScaling = false;
1528
1
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1529
1
    FontGlobalScale = 1.0f; // Use style.FontScaleMain instead!
1530
1
#endif
1531
1
    DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
1532
1533
    // Keyboard/Gamepad Navigation options
1534
1
    ConfigNavSwapGamepadButtons = false;
1535
1
    ConfigNavMoveSetMousePos = false;
1536
1
    ConfigNavCaptureKeyboard = true;
1537
1
    ConfigNavEscapeClearFocusItem = true;
1538
1
    ConfigNavEscapeClearFocusWindow = false;
1539
1
    ConfigNavCursorVisibleAuto = true;
1540
1
    ConfigNavCursorVisibleAlways = false;
1541
1542
    // Miscellaneous options
1543
1
    MouseDrawCursor = false;
1544
1
#ifdef __APPLE__
1545
1
    ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag
1546
#else
1547
    ConfigMacOSXBehaviors = false;
1548
#endif
1549
1
    ConfigInputTrickleEventQueue = true;
1550
1
    ConfigInputTextCursorBlink = true;
1551
1
    ConfigInputTextEnterKeepActive = false;
1552
1
    ConfigDragClickToInputText = false;
1553
1
    ConfigWindowsResizeFromEdges = true;
1554
1
    ConfigWindowsMoveFromTitleBarOnly = false;
1555
1
    ConfigWindowsCopyContentsWithCtrlC = false;
1556
1
    ConfigScrollbarScrollByPage = true;
1557
1
    ConfigMemoryCompactTimer = 60.0f;
1558
1
    ConfigDebugIsDebuggerPresent = false;
1559
1
    ConfigDebugHighlightIdConflicts = true;
1560
1
    ConfigDebugHighlightIdConflictsShowItemPicker = true;
1561
1
    ConfigDebugBeginReturnValueOnce = false;
1562
1
    ConfigDebugBeginReturnValueLoop = false;
1563
1564
1
    ConfigErrorRecovery = true;
1565
1
    ConfigErrorRecoveryEnableAssert = true;
1566
1
    ConfigErrorRecoveryEnableDebugLog = true;
1567
1
    ConfigErrorRecoveryEnableTooltip = true;
1568
1569
    // Inputs Behaviors
1570
1
    MouseDoubleClickTime = 0.30f;
1571
1
    MouseDoubleClickMaxDist = 6.0f;
1572
1
    MouseDragThreshold = 6.0f;
1573
1
    KeyRepeatDelay = 0.275f;
1574
1
    KeyRepeatRate = 0.050f;
1575
1576
    // Platform Functions
1577
    // Note: Initialize() will setup default clipboard/ime handlers.
1578
1
    BackendPlatformName = BackendRendererName = NULL;
1579
1
    BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;
1580
1581
    // Input (NB: we already have memset zero the entire structure!)
1582
1
    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1583
1
    MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
1584
1
    MouseSource = ImGuiMouseSource_Mouse;
1585
6
    for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
1586
156
    for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
1587
1
    AppAcceptingEvents = true;
1588
1
}
1589
1590
// Pass in translated ASCII characters for text input.
1591
// - with glfw you can get those from the callback set in glfwSetCharCallback()
1592
// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message
1593
// FIXME: Should in theory be called "AddCharacterEvent()" to be consistent with new API
1594
void ImGuiIO::AddInputCharacter(unsigned int c)
1595
0
{
1596
0
    IM_ASSERT(Ctx != NULL);
1597
0
    ImGuiContext& g = *Ctx;
1598
0
    if (c == 0 || !AppAcceptingEvents)
1599
0
        return;
1600
1601
0
    ImGuiInputEvent e;
1602
0
    e.Type = ImGuiInputEventType_Text;
1603
0
    e.Source = ImGuiInputSource_Keyboard;
1604
0
    e.EventId = g.InputEventsNextEventId++;
1605
0
    e.Text.Char = c;
1606
0
    g.InputEventsQueue.push_back(e);
1607
0
}
1608
1609
// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so
1610
// we should save the high surrogate.
1611
void ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)
1612
0
{
1613
0
    if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)
1614
0
        return;
1615
1616
0
    if ((c & 0xFC00) == 0xD800) // High surrogate, must save
1617
0
    {
1618
0
        if (InputQueueSurrogate != 0)
1619
0
            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1620
0
        InputQueueSurrogate = c;
1621
0
        return;
1622
0
    }
1623
1624
0
    ImWchar cp = c;
1625
0
    if (InputQueueSurrogate != 0)
1626
0
    {
1627
0
        if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate
1628
0
        {
1629
0
            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);
1630
0
        }
1631
0
        else
1632
0
        {
1633
0
#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF
1634
0
            cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar
1635
#else
1636
            cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);
1637
#endif
1638
0
        }
1639
1640
0
        InputQueueSurrogate = 0;
1641
0
    }
1642
0
    AddInputCharacter((unsigned)cp);
1643
0
}
1644
1645
void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)
1646
0
{
1647
0
    if (!AppAcceptingEvents)
1648
0
        return;
1649
0
    while (*utf8_chars != 0)
1650
0
    {
1651
0
        unsigned int c = 0;
1652
0
        utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);
1653
0
        AddInputCharacter(c);
1654
0
    }
1655
0
}
1656
1657
// Clear all incoming events.
1658
void ImGuiIO::ClearEventsQueue()
1659
0
{
1660
0
    IM_ASSERT(Ctx != NULL);
1661
0
    ImGuiContext& g = *Ctx;
1662
0
    g.InputEventsQueue.clear();
1663
0
}
1664
1665
// Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
1666
void ImGuiIO::ClearInputKeys()
1667
0
{
1668
0
    ImGuiContext& g = *Ctx;
1669
0
    for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
1670
0
    {
1671
0
        if (ImGui::IsMouseKey((ImGuiKey)key))
1672
0
            continue;
1673
0
        ImGuiKeyData* key_data = &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
1674
0
        key_data->Down = false;
1675
0
        key_data->DownDuration = -1.0f;
1676
0
        key_data->DownDurationPrev = -1.0f;
1677
0
    }
1678
0
    KeyCtrl = KeyShift = KeyAlt = KeySuper = false;
1679
0
    KeyMods = ImGuiMod_None;
1680
0
    InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().
1681
0
}
1682
1683
void ImGuiIO::ClearInputMouse()
1684
0
{
1685
0
    for (ImGuiKey key = ImGuiKey_Mouse_BEGIN; key < ImGuiKey_Mouse_END; key = (ImGuiKey)(key + 1))
1686
0
    {
1687
0
        ImGuiKeyData* key_data = &KeysData[key - ImGuiKey_NamedKey_BEGIN];
1688
0
        key_data->Down = false;
1689
0
        key_data->DownDuration = -1.0f;
1690
0
        key_data->DownDurationPrev = -1.0f;
1691
0
    }
1692
0
    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
1693
0
    for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)
1694
0
    {
1695
0
        MouseDown[n] = false;
1696
0
        MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;
1697
0
    }
1698
0
    MouseWheel = MouseWheelH = 0.0f;
1699
0
}
1700
1701
// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.
1702
// Current frame character buffer is now also cleared by ClearInputKeys().
1703
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1704
void ImGuiIO::ClearInputCharacters()
1705
0
{
1706
0
    InputQueueCharacters.resize(0);
1707
0
}
1708
#endif
1709
1710
static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)
1711
9.31k
{
1712
9.31k
    ImGuiContext& g = *ctx;
1713
9.78k
    for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)
1714
480
    {
1715
480
        ImGuiInputEvent* e = &g.InputEventsQueue[n];
1716
480
        if (e->Type != type)
1717
2
            continue;
1718
478
        if (type == ImGuiInputEventType_Key && e->Key.Key != arg)
1719
468
            continue;
1720
10
        if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)
1721
0
            continue;
1722
10
        return e;
1723
10
    }
1724
9.30k
    return NULL;
1725
9.31k
}
1726
1727
// Queue a new key down/up event.
1728
// - ImGuiKey key:       Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
1729
// - bool down:          Is the key down? use false to signify a key release.
1730
// - float analog_value: 0.0f..1.0f
1731
// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE.
1732
// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.
1733
void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)
1734
7.46k
{
1735
    //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }
1736
7.46k
    IM_ASSERT(Ctx != NULL);
1737
7.46k
    if (key == ImGuiKey_None || !AppAcceptingEvents)
1738
0
        return;
1739
7.46k
    ImGuiContext& g = *Ctx;
1740
7.46k
    IM_ASSERT(ImGui::IsNamedKeyOrMod(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.
1741
7.46k
    IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.
1742
1743
    // MacOS: swap Cmd(Super) and Ctrl
1744
7.46k
    if (g.IO.ConfigMacOSXBehaviors)
1745
7.46k
    {
1746
7.46k
        if (key == ImGuiMod_Super)          { key = ImGuiMod_Ctrl; }
1747
5.97k
        else if (key == ImGuiMod_Ctrl)      { key = ImGuiMod_Super; }
1748
4.47k
        else if (key == ImGuiKey_LeftSuper) { key = ImGuiKey_LeftCtrl; }
1749
4.47k
        else if (key == ImGuiKey_RightSuper){ key = ImGuiKey_RightCtrl; }
1750
4.47k
        else if (key == ImGuiKey_LeftCtrl)  { key = ImGuiKey_LeftSuper; }
1751
4.47k
        else if (key == ImGuiKey_RightCtrl) { key = ImGuiKey_RightSuper; }
1752
7.46k
    }
1753
1754
    // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)
1755
7.46k
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);
1756
7.46k
    const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);
1757
7.46k
    const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;
1758
7.46k
    const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;
1759
7.46k
    if (latest_key_down == down && latest_key_analog == analog_value)
1760
6.92k
        return;
1761
1762
    // Add event
1763
540
    ImGuiInputEvent e;
1764
540
    e.Type = ImGuiInputEventType_Key;
1765
540
    e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;
1766
540
    e.EventId = g.InputEventsNextEventId++;
1767
540
    e.Key.Key = key;
1768
540
    e.Key.Down = down;
1769
540
    e.Key.AnalogValue = analog_value;
1770
540
    g.InputEventsQueue.push_back(e);
1771
540
}
1772
1773
void ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)
1774
7.46k
{
1775
7.46k
    if (!AppAcceptingEvents)
1776
0
        return;
1777
7.46k
    AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);
1778
7.46k
}
1779
1780
// [Optional] Call after AddKeyEvent().
1781
// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.
1782
// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.
1783
void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)
1784
1.49k
{
1785
1.49k
    if (key == ImGuiKey_None)
1786
0
        return;
1787
1.49k
    IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512
1788
1.49k
    IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511
1789
1.49k
    IM_UNUSED(key);                 // Yet unused
1790
1.49k
    IM_UNUSED(native_keycode);      // Yet unused
1791
1.49k
    IM_UNUSED(native_scancode);     // Yet unused
1792
1.49k
    IM_UNUSED(native_legacy_index); // Yet unused
1793
1.49k
}
1794
1795
// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
1796
void ImGuiIO::SetAppAcceptingEvents(bool accepting_events)
1797
0
{
1798
0
    AppAcceptingEvents = accepting_events;
1799
0
}
1800
1801
// Queue a mouse move event
1802
void ImGuiIO::AddMousePosEvent(float x, float y)
1803
1.84k
{
1804
1.84k
    IM_ASSERT(Ctx != NULL);
1805
1.84k
    ImGuiContext& g = *Ctx;
1806
1.84k
    if (!AppAcceptingEvents)
1807
0
        return;
1808
1809
    // Apply same flooring as UpdateMouseInputs()
1810
1.84k
    ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);
1811
1812
    // Filter duplicate
1813
1.84k
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);
1814
1.84k
    const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;
1815
1.84k
    if (latest_pos.x == pos.x && latest_pos.y == pos.y)
1816
1.68k
        return;
1817
1818
154
    ImGuiInputEvent e;
1819
154
    e.Type = ImGuiInputEventType_MousePos;
1820
154
    e.Source = ImGuiInputSource_Mouse;
1821
154
    e.EventId = g.InputEventsNextEventId++;
1822
154
    e.MousePos.PosX = pos.x;
1823
154
    e.MousePos.PosY = pos.y;
1824
154
    e.MousePos.MouseSource = g.InputEventsNextMouseSource;
1825
154
    g.InputEventsQueue.push_back(e);
1826
154
}
1827
1828
void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)
1829
4
{
1830
4
    IM_ASSERT(Ctx != NULL);
1831
4
    ImGuiContext& g = *Ctx;
1832
4
    IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);
1833
4
    if (!AppAcceptingEvents)
1834
0
        return;
1835
1836
    // On MacOS X: Convert Ctrl(Super)+Left click into Right-click: handle held button.
1837
4
    if (ConfigMacOSXBehaviors && mouse_button == 0 && MouseCtrlLeftAsRightClick)
1838
0
    {
1839
        // Order of both statements matters: this event will still release mouse button 1
1840
0
        mouse_button = 1;
1841
0
        if (!down)
1842
0
            MouseCtrlLeftAsRightClick = false;
1843
0
    }
1844
1845
    // Filter duplicate
1846
4
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);
1847
4
    const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];
1848
4
    if (latest_button_down == down)
1849
0
        return;
1850
1851
    // On MacOS X: Convert Ctrl(Super)+Left click into Right-click.
1852
    // - Note that this is actual physical Ctrl which is ImGuiMod_Super for us.
1853
    // - At this point we want from !down to down, so this is handling the initial press.
1854
4
    if (ConfigMacOSXBehaviors && mouse_button == 0 && down)
1855
2
    {
1856
2
        const ImGuiInputEvent* latest_super_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)ImGuiMod_Super);
1857
2
        if (latest_super_event ? latest_super_event->Key.Down : g.IO.KeySuper)
1858
0
        {
1859
0
            IMGUI_DEBUG_LOG_IO("[io] Super+Left Click aliased into Right Click\n");
1860
0
            MouseCtrlLeftAsRightClick = true;
1861
0
            AddMouseButtonEvent(1, true); // This is just quicker to write that passing through, as we need to filter duplicate again.
1862
0
            return;
1863
0
        }
1864
2
    }
1865
1866
4
    ImGuiInputEvent e;
1867
4
    e.Type = ImGuiInputEventType_MouseButton;
1868
4
    e.Source = ImGuiInputSource_Mouse;
1869
4
    e.EventId = g.InputEventsNextEventId++;
1870
4
    e.MouseButton.Button = mouse_button;
1871
4
    e.MouseButton.Down = down;
1872
4
    e.MouseButton.MouseSource = g.InputEventsNextMouseSource;
1873
4
    g.InputEventsQueue.push_back(e);
1874
4
}
1875
1876
// Queue a mouse wheel event (some mouse/API may only have a Y component)
1877
void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)
1878
0
{
1879
0
    IM_ASSERT(Ctx != NULL);
1880
0
    ImGuiContext& g = *Ctx;
1881
1882
    // Filter duplicate (unlike most events, wheel values are relative and easy to filter)
1883
0
    if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))
1884
0
        return;
1885
1886
0
    ImGuiInputEvent e;
1887
0
    e.Type = ImGuiInputEventType_MouseWheel;
1888
0
    e.Source = ImGuiInputSource_Mouse;
1889
0
    e.EventId = g.InputEventsNextEventId++;
1890
0
    e.MouseWheel.WheelX = wheel_x;
1891
0
    e.MouseWheel.WheelY = wheel_y;
1892
0
    e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;
1893
0
    g.InputEventsQueue.push_back(e);
1894
0
}
1895
1896
// This is not a real event, the data is latched in order to be stored in actual Mouse events.
1897
// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.
1898
void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)
1899
128
{
1900
128
    IM_ASSERT(Ctx != NULL);
1901
128
    ImGuiContext& g = *Ctx;
1902
128
    g.InputEventsNextMouseSource = source;
1903
128
}
1904
1905
void ImGuiIO::AddFocusEvent(bool focused)
1906
1
{
1907
1
    IM_ASSERT(Ctx != NULL);
1908
1
    ImGuiContext& g = *Ctx;
1909
1910
    // Filter duplicate
1911
1
    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);
1912
1
    const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;
1913
1
    if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))
1914
1
        return;
1915
1916
0
    ImGuiInputEvent e;
1917
0
    e.Type = ImGuiInputEventType_Focus;
1918
0
    e.EventId = g.InputEventsNextEventId++;
1919
0
    e.AppFocused.Focused = focused;
1920
0
    g.InputEventsQueue.push_back(e);
1921
0
}
1922
1923
ImGuiPlatformIO::ImGuiPlatformIO()
1924
1
{
1925
    // Most fields are initialized with zero
1926
1
    memset(this, 0, sizeof(*this));
1927
1
    Platform_LocaleDecimalPoint = '.';
1928
1
}
1929
1930
//-----------------------------------------------------------------------------
1931
// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)
1932
//-----------------------------------------------------------------------------
1933
1934
ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)
1935
0
{
1936
0
    IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()
1937
0
    ImVec2 p_last = p1;
1938
0
    ImVec2 p_closest;
1939
0
    float p_closest_dist2 = FLT_MAX;
1940
0
    float t_step = 1.0f / (float)num_segments;
1941
0
    for (int i_step = 1; i_step <= num_segments; i_step++)
1942
0
    {
1943
0
        ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);
1944
0
        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1945
0
        float dist2 = ImLengthSqr(p - p_line);
1946
0
        if (dist2 < p_closest_dist2)
1947
0
        {
1948
0
            p_closest = p_line;
1949
0
            p_closest_dist2 = dist2;
1950
0
        }
1951
0
        p_last = p_current;
1952
0
    }
1953
0
    return p_closest;
1954
0
}
1955
1956
// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp
1957
static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1958
0
{
1959
0
    float dx = x4 - x1;
1960
0
    float dy = y4 - y1;
1961
0
    float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
1962
0
    float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
1963
0
    d2 = (d2 >= 0) ? d2 : -d2;
1964
0
    d3 = (d3 >= 0) ? d3 : -d3;
1965
0
    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1966
0
    {
1967
0
        ImVec2 p_current(x4, y4);
1968
0
        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);
1969
0
        float dist2 = ImLengthSqr(p - p_line);
1970
0
        if (dist2 < p_closest_dist2)
1971
0
        {
1972
0
            p_closest = p_line;
1973
0
            p_closest_dist2 = dist2;
1974
0
        }
1975
0
        p_last = p_current;
1976
0
    }
1977
0
    else if (level < 10)
1978
0
    {
1979
0
        float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;
1980
0
        float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;
1981
0
        float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;
1982
0
        float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;
1983
0
        float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;
1984
0
        float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;
1985
0
        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1986
0
        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1987
0
    }
1988
0
}
1989
1990
// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol
1991
// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.
1992
ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)
1993
0
{
1994
0
    IM_ASSERT(tess_tol > 0.0f);
1995
0
    ImVec2 p_last = p1;
1996
0
    ImVec2 p_closest;
1997
0
    float p_closest_dist2 = FLT_MAX;
1998
0
    ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);
1999
0
    return p_closest;
2000
0
}
2001
2002
ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)
2003
0
{
2004
0
    ImVec2 ap = p - a;
2005
0
    ImVec2 ab_dir = b - a;
2006
0
    float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;
2007
0
    if (dot < 0.0f)
2008
0
        return a;
2009
0
    float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;
2010
0
    if (dot > ab_len_sqr)
2011
0
        return b;
2012
0
    return a + ab_dir * dot / ab_len_sqr;
2013
0
}
2014
2015
bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
2016
0
{
2017
0
    bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;
2018
0
    bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;
2019
0
    bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;
2020
0
    return ((b1 == b2) && (b2 == b3));
2021
0
}
2022
2023
void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)
2024
0
{
2025
0
    ImVec2 v0 = b - a;
2026
0
    ImVec2 v1 = c - a;
2027
0
    ImVec2 v2 = p - a;
2028
0
    const float denom = v0.x * v1.y - v1.x * v0.y;
2029
0
    out_v = (v2.x * v1.y - v1.x * v2.y) / denom;
2030
0
    out_w = (v0.x * v2.y - v2.x * v0.y) / denom;
2031
0
    out_u = 1.0f - out_v - out_w;
2032
0
}
2033
2034
ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)
2035
0
{
2036
0
    ImVec2 proj_ab = ImLineClosestPoint(a, b, p);
2037
0
    ImVec2 proj_bc = ImLineClosestPoint(b, c, p);
2038
0
    ImVec2 proj_ca = ImLineClosestPoint(c, a, p);
2039
0
    float dist2_ab = ImLengthSqr(p - proj_ab);
2040
0
    float dist2_bc = ImLengthSqr(p - proj_bc);
2041
0
    float dist2_ca = ImLengthSqr(p - proj_ca);
2042
0
    float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));
2043
0
    if (m == dist2_ab)
2044
0
        return proj_ab;
2045
0
    if (m == dist2_bc)
2046
0
        return proj_bc;
2047
0
    return proj_ca;
2048
0
}
2049
2050
//-----------------------------------------------------------------------------
2051
// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)
2052
//-----------------------------------------------------------------------------
2053
2054
// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.
2055
int ImStricmp(const char* str1, const char* str2)
2056
0
{
2057
0
    int d;
2058
0
    while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }
2059
0
    return d;
2060
0
}
2061
2062
int ImStrnicmp(const char* str1, const char* str2, size_t count)
2063
0
{
2064
0
    int d = 0;
2065
0
    while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }
2066
0
    return d;
2067
0
}
2068
2069
void ImStrncpy(char* dst, const char* src, size_t count)
2070
0
{
2071
0
    if (count < 1)
2072
0
        return;
2073
0
    if (count > 1)
2074
0
        strncpy(dst, src, count - 1); // FIXME-OPT: strncpy not only doesn't guarantee 0-termination, it also always writes the whole array
2075
0
    dst[count - 1] = 0;
2076
0
}
2077
2078
char* ImStrdup(const char* str)
2079
3
{
2080
3
    size_t len = ImStrlen(str);
2081
3
    void* buf = IM_ALLOC(len + 1);
2082
3
    return (char*)memcpy(buf, (const void*)str, len + 1);
2083
3
}
2084
2085
void* ImMemdup(const void* src, size_t size)
2086
0
{
2087
0
    void* dst = IM_ALLOC(size);
2088
0
    return memcpy(dst, src, size);
2089
0
}
2090
2091
char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)
2092
0
{
2093
0
    size_t dst_buf_size = p_dst_size ? *p_dst_size : ImStrlen(dst) + 1;
2094
0
    size_t src_size = ImStrlen(src) + 1;
2095
0
    if (dst_buf_size < src_size)
2096
0
    {
2097
0
        IM_FREE(dst);
2098
0
        dst = (char*)IM_ALLOC(src_size);
2099
0
        if (p_dst_size)
2100
0
            *p_dst_size = src_size;
2101
0
    }
2102
0
    return (char*)memcpy(dst, (const void*)src, src_size);
2103
0
}
2104
2105
const char* ImStrchrRange(const char* str, const char* str_end, char c)
2106
58
{
2107
58
    const char* p = (const char*)ImMemchr(str, (int)c, str_end - str);
2108
58
    return p;
2109
58
}
2110
2111
int ImStrlenW(const ImWchar* str)
2112
0
{
2113
    //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit
2114
0
    int n = 0;
2115
0
    while (*str++) n++;
2116
0
    return n;
2117
0
}
2118
2119
// Find end-of-line. Return pointer will point to either first \n, either str_end.
2120
const char* ImStreolRange(const char* str, const char* str_end)
2121
0
{
2122
0
    const char* p = (const char*)ImMemchr(str, '\n', str_end - str);
2123
0
    return p ? p : str_end;
2124
0
}
2125
2126
const char* ImStrbol(const char* buf_mid_line, const char* buf_begin) // find beginning-of-line
2127
0
{
2128
0
    IM_ASSERT_PARANOID(buf_mid_line >= buf_begin && buf_mid_line <= buf_begin + ImStrlen(buf_begin));
2129
0
    while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n')
2130
0
        buf_mid_line--;
2131
0
    return buf_mid_line;
2132
0
}
2133
2134
const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)
2135
0
{
2136
0
    if (!needle_end)
2137
0
        needle_end = needle + ImStrlen(needle);
2138
2139
0
    const char un0 = (char)ImToUpper(*needle);
2140
0
    while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))
2141
0
    {
2142
0
        if (ImToUpper(*haystack) == un0)
2143
0
        {
2144
0
            const char* b = needle + 1;
2145
0
            for (const char* a = haystack + 1; b < needle_end; a++, b++)
2146
0
                if (ImToUpper(*a) != ImToUpper(*b))
2147
0
                    break;
2148
0
            if (b == needle_end)
2149
0
                return haystack;
2150
0
        }
2151
0
        haystack++;
2152
0
    }
2153
0
    return NULL;
2154
0
}
2155
2156
// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible.
2157
void ImStrTrimBlanks(char* buf)
2158
0
{
2159
0
    char* p = buf;
2160
0
    while (p[0] == ' ' || p[0] == '\t')     // Leading blanks
2161
0
        p++;
2162
0
    char* p_start = p;
2163
0
    while (*p != 0)                         // Find end of string
2164
0
        p++;
2165
0
    while (p > p_start && (p[-1] == ' ' || p[-1] == '\t'))  // Trailing blanks
2166
0
        p--;
2167
0
    if (p_start != buf)                     // Copy memory if we had leading blanks
2168
0
        memmove(buf, p_start, p - p_start);
2169
0
    buf[p - p_start] = 0;                   // Zero terminate
2170
0
}
2171
2172
const char* ImStrSkipBlank(const char* str)
2173
52
{
2174
104
    while (str[0] == ' ' || str[0] == '\t')
2175
52
        str++;
2176
52
    return str;
2177
52
}
2178
2179
// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).
2180
// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.
2181
// B) When buf==NULL vsnprintf() will return the output size.
2182
#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2183
2184
// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)
2185
// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2186
// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are
2187
// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)
2188
#ifdef IMGUI_USE_STB_SPRINTF
2189
#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION
2190
#define STB_SPRINTF_IMPLEMENTATION
2191
#endif
2192
#ifdef IMGUI_STB_SPRINTF_FILENAME
2193
#include IMGUI_STB_SPRINTF_FILENAME
2194
#else
2195
#include "stb_sprintf.h"
2196
#endif
2197
#endif // #ifdef IMGUI_USE_STB_SPRINTF
2198
2199
#if defined(_MSC_VER) && !defined(vsnprintf)
2200
#define vsnprintf _vsnprintf
2201
#endif
2202
2203
int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)
2204
661
{
2205
661
    va_list args;
2206
661
    va_start(args, fmt);
2207
#ifdef IMGUI_USE_STB_SPRINTF
2208
    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2209
#else
2210
661
    int w = vsnprintf(buf, buf_size, fmt, args);
2211
661
#endif
2212
661
    va_end(args);
2213
661
    if (buf == NULL)
2214
0
        return w;
2215
661
    if (w == -1 || w >= (int)buf_size)
2216
0
        w = (int)buf_size - 1;
2217
661
    buf[w] = 0;
2218
661
    return w;
2219
661
}
2220
2221
int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
2222
226
{
2223
#ifdef IMGUI_USE_STB_SPRINTF
2224
    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
2225
#else
2226
226
    int w = vsnprintf(buf, buf_size, fmt, args);
2227
226
#endif
2228
226
    if (buf == NULL)
2229
113
        return w;
2230
113
    if (w == -1 || w >= (int)buf_size)
2231
0
        w = (int)buf_size - 1;
2232
113
    buf[w] = 0;
2233
113
    return w;
2234
226
}
2235
#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
2236
2237
void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)
2238
0
{
2239
0
    va_list args;
2240
0
    va_start(args, fmt);
2241
0
    ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);
2242
0
    va_end(args);
2243
0
}
2244
2245
// FIXME: Should rework API toward allowing multiple in-flight temp buffers (easier and safer for caller)
2246
// by making the caller acquire a temp buffer token, with either explicit or destructor release, e.g.
2247
//  ImGuiTempBufferToken token;
2248
//  ImFormatStringToTempBuffer(token, ...);
2249
void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)
2250
0
{
2251
0
    ImGuiContext& g = *GImGui;
2252
0
    if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)
2253
0
    {
2254
0
        const char* buf = va_arg(args, const char*); // Skip formatting when using "%s"
2255
0
        if (buf == NULL)
2256
0
            buf = "(null)";
2257
0
        *out_buf = buf;
2258
0
        if (out_buf_end) { *out_buf_end = buf + ImStrlen(buf); }
2259
0
    }
2260
0
    else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)
2261
0
    {
2262
0
        int buf_len = va_arg(args, int); // Skip formatting when using "%.*s"
2263
0
        const char* buf = va_arg(args, const char*);
2264
0
        if (buf == NULL)
2265
0
        {
2266
0
            buf = "(null)";
2267
0
            buf_len = ImMin(buf_len, 6);
2268
0
        }
2269
0
        *out_buf = buf;
2270
0
        *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.
2271
0
    }
2272
0
    else
2273
0
    {
2274
0
        int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);
2275
0
        *out_buf = g.TempBuffer.Data;
2276
0
        if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }
2277
0
    }
2278
0
}
2279
2280
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2281
// CRC32 needs a 1KB lookup table (not cache friendly)
2282
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
2283
// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
2284
static const ImU32 GCrc32LookupTable[256] =
2285
{
2286
#ifdef IMGUI_USE_LEGACY_CRC32_ADLER
2287
    // Legacy CRC32-adler table used pre 1.91.6 (before 2024/11/27). Only use if you cannot afford invalidating old .ini data.
2288
    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
2289
    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,
2290
    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,
2291
    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,
2292
    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,
2293
    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,
2294
    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,
2295
    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,
2296
    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,
2297
    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,
2298
    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,
2299
    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,
2300
    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,
2301
    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,
2302
    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,
2303
    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
2304
#else
2305
    // CRC32c table compatible with SSE 4.2 instructions
2306
    0x00000000,0xF26B8303,0xE13B70F7,0x1350F3F4,0xC79A971F,0x35F1141C,0x26A1E7E8,0xD4CA64EB,0x8AD958CF,0x78B2DBCC,0x6BE22838,0x9989AB3B,0x4D43CFD0,0xBF284CD3,0xAC78BF27,0x5E133C24,
2307
    0x105EC76F,0xE235446C,0xF165B798,0x030E349B,0xD7C45070,0x25AFD373,0x36FF2087,0xC494A384,0x9A879FA0,0x68EC1CA3,0x7BBCEF57,0x89D76C54,0x5D1D08BF,0xAF768BBC,0xBC267848,0x4E4DFB4B,
2308
    0x20BD8EDE,0xD2D60DDD,0xC186FE29,0x33ED7D2A,0xE72719C1,0x154C9AC2,0x061C6936,0xF477EA35,0xAA64D611,0x580F5512,0x4B5FA6E6,0xB93425E5,0x6DFE410E,0x9F95C20D,0x8CC531F9,0x7EAEB2FA,
2309
    0x30E349B1,0xC288CAB2,0xD1D83946,0x23B3BA45,0xF779DEAE,0x05125DAD,0x1642AE59,0xE4292D5A,0xBA3A117E,0x4851927D,0x5B016189,0xA96AE28A,0x7DA08661,0x8FCB0562,0x9C9BF696,0x6EF07595,
2310
    0x417B1DBC,0xB3109EBF,0xA0406D4B,0x522BEE48,0x86E18AA3,0x748A09A0,0x67DAFA54,0x95B17957,0xCBA24573,0x39C9C670,0x2A993584,0xD8F2B687,0x0C38D26C,0xFE53516F,0xED03A29B,0x1F682198,
2311
    0x5125DAD3,0xA34E59D0,0xB01EAA24,0x42752927,0x96BF4DCC,0x64D4CECF,0x77843D3B,0x85EFBE38,0xDBFC821C,0x2997011F,0x3AC7F2EB,0xC8AC71E8,0x1C661503,0xEE0D9600,0xFD5D65F4,0x0F36E6F7,
2312
    0x61C69362,0x93AD1061,0x80FDE395,0x72966096,0xA65C047D,0x5437877E,0x4767748A,0xB50CF789,0xEB1FCBAD,0x197448AE,0x0A24BB5A,0xF84F3859,0x2C855CB2,0xDEEEDFB1,0xCDBE2C45,0x3FD5AF46,
2313
    0x7198540D,0x83F3D70E,0x90A324FA,0x62C8A7F9,0xB602C312,0x44694011,0x5739B3E5,0xA55230E6,0xFB410CC2,0x092A8FC1,0x1A7A7C35,0xE811FF36,0x3CDB9BDD,0xCEB018DE,0xDDE0EB2A,0x2F8B6829,
2314
    0x82F63B78,0x709DB87B,0x63CD4B8F,0x91A6C88C,0x456CAC67,0xB7072F64,0xA457DC90,0x563C5F93,0x082F63B7,0xFA44E0B4,0xE9141340,0x1B7F9043,0xCFB5F4A8,0x3DDE77AB,0x2E8E845F,0xDCE5075C,
2315
    0x92A8FC17,0x60C37F14,0x73938CE0,0x81F80FE3,0x55326B08,0xA759E80B,0xB4091BFF,0x466298FC,0x1871A4D8,0xEA1A27DB,0xF94AD42F,0x0B21572C,0xDFEB33C7,0x2D80B0C4,0x3ED04330,0xCCBBC033,
2316
    0xA24BB5A6,0x502036A5,0x4370C551,0xB11B4652,0x65D122B9,0x97BAA1BA,0x84EA524E,0x7681D14D,0x2892ED69,0xDAF96E6A,0xC9A99D9E,0x3BC21E9D,0xEF087A76,0x1D63F975,0x0E330A81,0xFC588982,
2317
    0xB21572C9,0x407EF1CA,0x532E023E,0xA145813D,0x758FE5D6,0x87E466D5,0x94B49521,0x66DF1622,0x38CC2A06,0xCAA7A905,0xD9F75AF1,0x2B9CD9F2,0xFF56BD19,0x0D3D3E1A,0x1E6DCDEE,0xEC064EED,
2318
    0xC38D26C4,0x31E6A5C7,0x22B65633,0xD0DDD530,0x0417B1DB,0xF67C32D8,0xE52CC12C,0x1747422F,0x49547E0B,0xBB3FFD08,0xA86F0EFC,0x5A048DFF,0x8ECEE914,0x7CA56A17,0x6FF599E3,0x9D9E1AE0,
2319
    0xD3D3E1AB,0x21B862A8,0x32E8915C,0xC083125F,0x144976B4,0xE622F5B7,0xF5720643,0x07198540,0x590AB964,0xAB613A67,0xB831C993,0x4A5A4A90,0x9E902E7B,0x6CFBAD78,0x7FAB5E8C,0x8DC0DD8F,
2320
    0xE330A81A,0x115B2B19,0x020BD8ED,0xF0605BEE,0x24AA3F05,0xD6C1BC06,0xC5914FF2,0x37FACCF1,0x69E9F0D5,0x9B8273D6,0x88D28022,0x7AB90321,0xAE7367CA,0x5C18E4C9,0x4F48173D,0xBD23943E,
2321
    0xF36E6F75,0x0105EC76,0x12551F82,0xE03E9C81,0x34F4F86A,0xC69F7B69,0xD5CF889D,0x27A40B9E,0x79B737BA,0x8BDCB4B9,0x988C474D,0x6AE7C44E,0xBE2DA0A5,0x4C4623A6,0x5F16D052,0xAD7D5351
2322
#endif
2323
};
2324
#endif
2325
2326
// Known size hash
2327
// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
2328
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2329
ImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)
2330
80.5k
{
2331
80.5k
    ImU32 crc = ~seed;
2332
80.5k
    const unsigned char* data = (const unsigned char*)data_p;
2333
80.5k
    const unsigned char *data_end = (const unsigned char*)data_p + data_size;
2334
80.5k
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2335
80.5k
    const ImU32* crc32_lut = GCrc32LookupTable;
2336
1.04M
    while (data < data_end)
2337
967k
        crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
2338
80.5k
    return ~crc;
2339
#else
2340
    while (data + 4 <= data_end)
2341
    {
2342
        crc = _mm_crc32_u32(crc, *(ImU32*)data);
2343
        data += 4;
2344
    }
2345
    while (data < data_end)
2346
        crc = _mm_crc32_u8(crc, *data++);
2347
    return ~crc;
2348
#endif
2349
80.5k
}
2350
2351
// Zero-terminated string hash, with support for ### to reset back to seed value
2352
// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
2353
// Because this syntax is rarely used we are optimizing for the common case.
2354
// - If we reach ### in the string we discard the hash so far and reset to the seed.
2355
// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
2356
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
2357
ImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)
2358
1.21M
{
2359
1.21M
    seed = ~seed;
2360
1.21M
    ImU32 crc = seed;
2361
1.21M
    const unsigned char* data = (const unsigned char*)data_p;
2362
1.21M
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2363
1.21M
    const ImU32* crc32_lut = GCrc32LookupTable;
2364
1.21M
#endif
2365
1.21M
    if (data_size != 0)
2366
3
    {
2367
80
        while (data_size-- != 0)
2368
77
        {
2369
77
            unsigned char c = *data++;
2370
77
            if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')
2371
0
                crc = seed;
2372
77
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2373
77
            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2374
#else
2375
            crc = _mm_crc32_u8(crc, c);
2376
#endif
2377
77
        }
2378
3
    }
2379
1.21M
    else
2380
1.21M
    {
2381
9.77M
        while (unsigned char c = *data++)
2382
8.56M
        {
2383
8.56M
            if (c == '#' && data[0] == '#' && data[1] == '#')
2384
661
                crc = seed;
2385
8.56M
#ifndef IMGUI_ENABLE_SSE4_2_CRC
2386
8.56M
            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
2387
#else
2388
            crc = _mm_crc32_u8(crc, c);
2389
#endif
2390
8.56M
        }
2391
1.21M
    }
2392
1.21M
    return ~crc;
2393
1.21M
}
2394
2395
// Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
2396
// FIXME-OPT: This is not designed to be optimal. Use with care.
2397
const char* ImHashSkipUncontributingPrefix(const char* label)
2398
3
{
2399
3
    const char* result = label;
2400
80
    while (unsigned char c = *label++)
2401
77
        if (c == '#' && label[0] == '#' && label[1] == '#')
2402
0
            result = label - 1;
2403
3
    return result;
2404
3
}
2405
2406
//-----------------------------------------------------------------------------
2407
// [SECTION] MISC HELPERS/UTILITIES (File functions)
2408
//-----------------------------------------------------------------------------
2409
2410
// Default file functions
2411
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2412
2413
ImFileHandle ImFileOpen(const char* filename, const char* mode)
2414
2
{
2415
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && (defined(__MINGW32__) || (!defined(__CYGWIN__) && !defined(__GNUC__)))
2416
    // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.
2417
    // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!
2418
    const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
2419
    const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
2420
2421
    // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator.
2422
    // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314).
2423
    wchar_t local_temp_stack[FILENAME_MAX];
2424
    ImVector<wchar_t> local_temp_heap;
2425
    if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack))
2426
        local_temp_heap.resize(filename_wsize + mode_wsize);
2427
    wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack;
2428
    wchar_t* mode_wbuf = filename_wbuf + filename_wsize;
2429
    ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize);
2430
    ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize);
2431
    return ::_wfopen(filename_wbuf, mode_wbuf);
2432
#else
2433
2
    return fopen(filename, mode);
2434
2
#endif
2435
2
}
2436
2437
// We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.
2438
2
bool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }
2439
1
ImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }
2440
1
ImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }
2441
1
ImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }
2442
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
2443
2444
// Helper: Load file content into memory
2445
// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()
2446
// This can't really be used with "rt" because fseek size won't match read size.
2447
void*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)
2448
1
{
2449
1
    IM_ASSERT(filename && mode);
2450
1
    if (out_file_size)
2451
1
        *out_file_size = 0;
2452
2453
1
    ImFileHandle f;
2454
1
    if ((f = ImFileOpen(filename, mode)) == NULL)
2455
0
        return NULL;
2456
2457
1
    size_t file_size = (size_t)ImFileGetSize(f);
2458
1
    if (file_size == (size_t)-1)
2459
0
    {
2460
0
        ImFileClose(f);
2461
0
        return NULL;
2462
0
    }
2463
2464
1
    void* file_data = IM_ALLOC(file_size + padding_bytes);
2465
1
    if (file_data == NULL)
2466
0
    {
2467
0
        ImFileClose(f);
2468
0
        return NULL;
2469
0
    }
2470
1
    if (ImFileRead(file_data, 1, file_size, f) != file_size)
2471
0
    {
2472
0
        ImFileClose(f);
2473
0
        IM_FREE(file_data);
2474
0
        return NULL;
2475
0
    }
2476
1
    if (padding_bytes > 0)
2477
0
        memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);
2478
2479
1
    ImFileClose(f);
2480
1
    if (out_file_size)
2481
1
        *out_file_size = file_size;
2482
2483
1
    return file_data;
2484
1
}
2485
2486
//-----------------------------------------------------------------------------
2487
// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)
2488
//-----------------------------------------------------------------------------
2489
2490
IM_MSVC_RUNTIME_CHECKS_OFF
2491
2492
// Convert UTF-8 to 32-bit character, process single character input.
2493
// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).
2494
// We handle UTF-8 decoding error by skipping forward.
2495
int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)
2496
0
{
2497
0
    static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };
2498
0
    static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };
2499
0
    static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };
2500
0
    static const int shiftc[] = { 0, 18, 12, 6, 0 };
2501
0
    static const int shifte[] = { 0, 6, 4, 2, 0 };
2502
0
    int len = lengths[*(const unsigned char*)in_text >> 3];
2503
0
    int wanted = len + (len ? 0 : 1);
2504
2505
0
    if (in_text_end == NULL)
2506
0
        in_text_end = in_text + wanted; // Max length, nulls will be taken into account.
2507
2508
    // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,
2509
    // so it is fast even with excessive branching.
2510
0
    unsigned char s[4];
2511
0
    s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;
2512
0
    s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;
2513
0
    s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;
2514
0
    s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;
2515
2516
    // Assume a four-byte character and load four bytes. Unused bits are shifted out.
2517
0
    *out_char  = (uint32_t)(s[0] & masks[len]) << 18;
2518
0
    *out_char |= (uint32_t)(s[1] & 0x3f) << 12;
2519
0
    *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;
2520
0
    *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;
2521
0
    *out_char >>= shiftc[len];
2522
2523
    // Accumulate the various error conditions.
2524
0
    int e = 0;
2525
0
    e  = (*out_char < mins[len]) << 6; // non-canonical encoding
2526
0
    e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?
2527
0
    e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range we can store in ImWchar (FIXME: May evolve)
2528
0
    e |= (s[1] & 0xc0) >> 2;
2529
0
    e |= (s[2] & 0xc0) >> 4;
2530
0
    e |= (s[3]       ) >> 6;
2531
0
    e ^= 0x2a; // top two bits of each tail byte correct?
2532
0
    e >>= shifte[len];
2533
2534
0
    if (e)
2535
0
    {
2536
        // No bytes are consumed when *in_text == 0 || in_text == in_text_end.
2537
        // One byte is consumed in case of invalid first byte of in_text.
2538
        // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.
2539
        // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.
2540
0
        wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);
2541
0
        *out_char = IM_UNICODE_CODEPOINT_INVALID;
2542
0
    }
2543
2544
0
    return wanted;
2545
0
}
2546
2547
int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)
2548
0
{
2549
0
    ImWchar* buf_out = buf;
2550
0
    ImWchar* buf_end = buf + buf_size;
2551
0
    while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2552
0
    {
2553
0
        unsigned int c;
2554
0
        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2555
0
        *buf_out++ = (ImWchar)c;
2556
0
    }
2557
0
    *buf_out = 0;
2558
0
    if (in_text_remaining)
2559
0
        *in_text_remaining = in_text;
2560
0
    return (int)(buf_out - buf);
2561
0
}
2562
2563
int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)
2564
0
{
2565
0
    int char_count = 0;
2566
0
    while ((!in_text_end || in_text < in_text_end) && *in_text)
2567
0
    {
2568
0
        unsigned int c;
2569
0
        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);
2570
0
        char_count++;
2571
0
    }
2572
0
    return char_count;
2573
0
}
2574
2575
// Based on stb_to_utf8() from github.com/nothings/stb/
2576
static inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)
2577
0
{
2578
0
    if (c < 0x80)
2579
0
    {
2580
0
        buf[0] = (char)c;
2581
0
        return 1;
2582
0
    }
2583
0
    if (c < 0x800)
2584
0
    {
2585
0
        if (buf_size < 2) return 0;
2586
0
        buf[0] = (char)(0xc0 + (c >> 6));
2587
0
        buf[1] = (char)(0x80 + (c & 0x3f));
2588
0
        return 2;
2589
0
    }
2590
0
    if (c < 0x10000)
2591
0
    {
2592
0
        if (buf_size < 3) return 0;
2593
0
        buf[0] = (char)(0xe0 + (c >> 12));
2594
0
        buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));
2595
0
        buf[2] = (char)(0x80 + ((c ) & 0x3f));
2596
0
        return 3;
2597
0
    }
2598
0
    if (c <= 0x10FFFF)
2599
0
    {
2600
0
        if (buf_size < 4) return 0;
2601
0
        buf[0] = (char)(0xf0 + (c >> 18));
2602
0
        buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));
2603
0
        buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));
2604
0
        buf[3] = (char)(0x80 + ((c ) & 0x3f));
2605
0
        return 4;
2606
0
    }
2607
    // Invalid code point, the max unicode is 0x10FFFF
2608
0
    return 0;
2609
0
}
2610
2611
int ImTextCharToUtf8(char out_buf[5], unsigned int c)
2612
0
{
2613
0
    int count = ImTextCharToUtf8_inline(out_buf, 5, c);
2614
0
    out_buf[count] = 0;
2615
0
    return count;
2616
0
}
2617
2618
// Not optimal but we very rarely use this function.
2619
int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)
2620
0
{
2621
0
    unsigned int unused = 0;
2622
0
    return ImTextCharFromUtf8(&unused, in_text, in_text_end);
2623
0
}
2624
2625
static inline int ImTextCountUtf8BytesFromChar(unsigned int c)
2626
0
{
2627
0
    if (c < 0x80) return 1;
2628
0
    if (c < 0x800) return 2;
2629
0
    if (c < 0x10000) return 3;
2630
0
    if (c <= 0x10FFFF) return 4;
2631
0
    return 3;
2632
0
}
2633
2634
int ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)
2635
0
{
2636
0
    char* buf_p = out_buf;
2637
0
    const char* buf_end = out_buf + out_buf_size;
2638
0
    while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)
2639
0
    {
2640
0
        unsigned int c = (unsigned int)(*in_text++);
2641
0
        if (c < 0x80)
2642
0
            *buf_p++ = (char)c;
2643
0
        else
2644
0
            buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);
2645
0
    }
2646
0
    *buf_p = 0;
2647
0
    return (int)(buf_p - out_buf);
2648
0
}
2649
2650
int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)
2651
0
{
2652
0
    int bytes_count = 0;
2653
0
    while ((!in_text_end || in_text < in_text_end) && *in_text)
2654
0
    {
2655
0
        unsigned int c = (unsigned int)(*in_text++);
2656
0
        if (c < 0x80)
2657
0
            bytes_count++;
2658
0
        else
2659
0
            bytes_count += ImTextCountUtf8BytesFromChar(c);
2660
0
    }
2661
0
    return bytes_count;
2662
0
}
2663
2664
const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)
2665
0
{
2666
0
    while (in_text_curr > in_text_start)
2667
0
    {
2668
0
        in_text_curr--;
2669
0
        if ((*in_text_curr & 0xC0) != 0x80)
2670
0
            return in_text_curr;
2671
0
    }
2672
0
    return in_text_start;
2673
0
}
2674
2675
int ImTextCountLines(const char* in_text, const char* in_text_end)
2676
0
{
2677
0
    if (in_text_end == NULL)
2678
0
        in_text_end = in_text + ImStrlen(in_text); // FIXME-OPT: Not optimal approach, discourage use for now.
2679
0
    int count = 0;
2680
0
    while (in_text < in_text_end)
2681
0
    {
2682
0
        const char* line_end = (const char*)ImMemchr(in_text, '\n', in_text_end - in_text);
2683
0
        in_text = line_end ? line_end + 1 : in_text_end;
2684
0
        count++;
2685
0
    }
2686
0
    return count;
2687
0
}
2688
2689
IM_MSVC_RUNTIME_CHECKS_RESTORE
2690
2691
//-----------------------------------------------------------------------------
2692
// [SECTION] MISC HELPERS/UTILITIES (Color functions)
2693
// Note: The Convert functions are early design which are not consistent with other API.
2694
//-----------------------------------------------------------------------------
2695
2696
IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)
2697
0
{
2698
0
    float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;
2699
0
    int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);
2700
0
    int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);
2701
0
    int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);
2702
0
    return IM_COL32(r, g, b, 0xFF);
2703
0
}
2704
2705
ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)
2706
0
{
2707
0
    float s = 1.0f / 255.0f;
2708
0
    return ImVec4(
2709
0
        ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
2710
0
        ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
2711
0
        ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
2712
0
        ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
2713
0
}
2714
2715
ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)
2716
972k
{
2717
972k
    ImU32 out;
2718
972k
    out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;
2719
972k
    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;
2720
972k
    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;
2721
972k
    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;
2722
972k
    return out;
2723
972k
}
2724
2725
// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592
2726
// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
2727
void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)
2728
0
{
2729
0
    float K = 0.f;
2730
0
    if (g < b)
2731
0
    {
2732
0
        ImSwap(g, b);
2733
0
        K = -1.f;
2734
0
    }
2735
0
    if (r < g)
2736
0
    {
2737
0
        ImSwap(r, g);
2738
0
        K = -2.f / 6.f - K;
2739
0
    }
2740
2741
0
    const float chroma = r - (g < b ? g : b);
2742
0
    out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));
2743
0
    out_s = chroma / (r + 1e-20f);
2744
0
    out_v = r;
2745
0
}
2746
2747
// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593
2748
// also http://en.wikipedia.org/wiki/HSL_and_HSV
2749
void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)
2750
0
{
2751
0
    if (s == 0.0f)
2752
0
    {
2753
        // gray
2754
0
        out_r = out_g = out_b = v;
2755
0
        return;
2756
0
    }
2757
2758
0
    h = ImFmod(h, 1.0f) / (60.0f / 360.0f);
2759
0
    int   i = (int)h;
2760
0
    float f = h - (float)i;
2761
0
    float p = v * (1.0f - s);
2762
0
    float q = v * (1.0f - s * f);
2763
0
    float t = v * (1.0f - s * (1.0f - f));
2764
2765
0
    switch (i)
2766
0
    {
2767
0
    case 0: out_r = v; out_g = t; out_b = p; break;
2768
0
    case 1: out_r = q; out_g = v; out_b = p; break;
2769
0
    case 2: out_r = p; out_g = v; out_b = t; break;
2770
0
    case 3: out_r = p; out_g = q; out_b = v; break;
2771
0
    case 4: out_r = t; out_g = p; out_b = v; break;
2772
0
    case 5: default: out_r = v; out_g = p; out_b = q; break;
2773
0
    }
2774
0
}
2775
2776
//-----------------------------------------------------------------------------
2777
// [SECTION] ImGuiStorage
2778
// Helper: Key->value storage
2779
//-----------------------------------------------------------------------------
2780
2781
// std::lower_bound but without the bullshit
2782
ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key)
2783
323k
{
2784
323k
    ImGuiStoragePair* in_p = in_begin;
2785
888k
    for (size_t count = (size_t)(in_end - in_p); count > 0; )
2786
565k
    {
2787
565k
        size_t count2 = count >> 1;
2788
565k
        ImGuiStoragePair* mid = in_p + count2;
2789
565k
        if (mid->key < key)
2790
81.2k
        {
2791
81.2k
            in_p = ++mid;
2792
81.2k
            count -= count2 + 1;
2793
81.2k
        }
2794
484k
        else
2795
484k
        {
2796
484k
            count = count2;
2797
484k
        }
2798
565k
    }
2799
323k
    return in_p;
2800
323k
}
2801
2802
IM_MSVC_RUNTIME_CHECKS_OFF
2803
static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)
2804
0
{
2805
    // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.
2806
0
    ImGuiID lhs_v = ((const ImGuiStoragePair*)lhs)->key;
2807
0
    ImGuiID rhs_v = ((const ImGuiStoragePair*)rhs)->key;
2808
0
    return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
2809
0
}
2810
2811
// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2812
void ImGuiStorage::BuildSortByKey()
2813
0
{
2814
0
    ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), PairComparerByID);
2815
0
}
2816
2817
int ImGuiStorage::GetInt(ImGuiID key, int default_val) const
2818
0
{
2819
0
    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2820
0
    if (it == Data.Data + Data.Size || it->key != key)
2821
0
        return default_val;
2822
0
    return it->val_i;
2823
0
}
2824
2825
bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const
2826
0
{
2827
0
    return GetInt(key, default_val ? 1 : 0) != 0;
2828
0
}
2829
2830
float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const
2831
0
{
2832
0
    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2833
0
    if (it == Data.Data + Data.Size || it->key != key)
2834
0
        return default_val;
2835
0
    return it->val_f;
2836
0
}
2837
2838
void* ImGuiStorage::GetVoidPtr(ImGuiID key) const
2839
242k
{
2840
242k
    ImGuiStoragePair* it = ImLowerBound(const_cast<ImGuiStoragePair*>(Data.Data), const_cast<ImGuiStoragePair*>(Data.Data + Data.Size), key);
2841
242k
    if (it == Data.Data + Data.Size || it->key != key)
2842
7
        return NULL;
2843
242k
    return it->val_p;
2844
242k
}
2845
2846
// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
2847
int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)
2848
0
{
2849
0
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2850
0
    if (it == Data.Data + Data.Size || it->key != key)
2851
0
        it = Data.insert(it, ImGuiStoragePair(key, default_val));
2852
0
    return &it->val_i;
2853
0
}
2854
2855
bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)
2856
0
{
2857
0
    return (bool*)GetIntRef(key, default_val ? 1 : 0);
2858
0
}
2859
2860
float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)
2861
0
{
2862
0
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2863
0
    if (it == Data.Data + Data.Size || it->key != key)
2864
0
        it = Data.insert(it, ImGuiStoragePair(key, default_val));
2865
0
    return &it->val_f;
2866
0
}
2867
2868
void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)
2869
80.5k
{
2870
80.5k
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2871
80.5k
    if (it == Data.Data + Data.Size || it->key != key)
2872
1
        it = Data.insert(it, ImGuiStoragePair(key, default_val));
2873
80.5k
    return &it->val_p;
2874
80.5k
}
2875
2876
// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)
2877
void ImGuiStorage::SetInt(ImGuiID key, int val)
2878
0
{
2879
0
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2880
0
    if (it == Data.Data + Data.Size || it->key != key)
2881
0
        Data.insert(it, ImGuiStoragePair(key, val));
2882
0
    else
2883
0
        it->val_i = val;
2884
0
}
2885
2886
void ImGuiStorage::SetBool(ImGuiID key, bool val)
2887
0
{
2888
0
    SetInt(key, val ? 1 : 0);
2889
0
}
2890
2891
void ImGuiStorage::SetFloat(ImGuiID key, float val)
2892
0
{
2893
0
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2894
0
    if (it == Data.Data + Data.Size || it->key != key)
2895
0
        Data.insert(it, ImGuiStoragePair(key, val));
2896
0
    else
2897
0
        it->val_f = val;
2898
0
}
2899
2900
void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)
2901
4
{
2902
4
    ImGuiStoragePair* it = ImLowerBound(Data.Data, Data.Data + Data.Size, key);
2903
4
    if (it == Data.Data + Data.Size || it->key != key)
2904
3
        Data.insert(it, ImGuiStoragePair(key, val));
2905
1
    else
2906
1
        it->val_p = val;
2907
4
}
2908
2909
void ImGuiStorage::SetAllInt(int v)
2910
0
{
2911
0
    for (int i = 0; i < Data.Size; i++)
2912
0
        Data[i].val_i = v;
2913
0
}
2914
IM_MSVC_RUNTIME_CHECKS_RESTORE
2915
2916
//-----------------------------------------------------------------------------
2917
// [SECTION] ImGuiTextFilter
2918
//-----------------------------------------------------------------------------
2919
2920
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2921
ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077
2922
0
{
2923
0
    InputBuf[0] = 0;
2924
0
    CountGrep = 0;
2925
0
    if (default_filter)
2926
0
    {
2927
0
        ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));
2928
0
        Build();
2929
0
    }
2930
0
}
2931
2932
bool ImGuiTextFilter::Draw(const char* label, float width)
2933
0
{
2934
0
    if (width != 0.0f)
2935
0
        ImGui::SetNextItemWidth(width);
2936
0
    bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));
2937
0
    if (value_changed)
2938
0
        Build();
2939
0
    return value_changed;
2940
0
}
2941
2942
void ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const
2943
0
{
2944
0
    out->resize(0);
2945
0
    const char* wb = b;
2946
0
    const char* we = wb;
2947
0
    while (we < e)
2948
0
    {
2949
0
        if (*we == separator)
2950
0
        {
2951
0
            out->push_back(ImGuiTextRange(wb, we));
2952
0
            wb = we + 1;
2953
0
        }
2954
0
        we++;
2955
0
    }
2956
0
    if (wb != we)
2957
0
        out->push_back(ImGuiTextRange(wb, we));
2958
0
}
2959
2960
void ImGuiTextFilter::Build()
2961
0
{
2962
0
    Filters.resize(0);
2963
0
    ImGuiTextRange input_range(InputBuf, InputBuf + ImStrlen(InputBuf));
2964
0
    input_range.split(',', &Filters);
2965
2966
0
    CountGrep = 0;
2967
0
    for (ImGuiTextRange& f : Filters)
2968
0
    {
2969
0
        while (f.b < f.e && ImCharIsBlankA(f.b[0]))
2970
0
            f.b++;
2971
0
        while (f.e > f.b && ImCharIsBlankA(f.e[-1]))
2972
0
            f.e--;
2973
0
        if (f.empty())
2974
0
            continue;
2975
0
        if (f.b[0] != '-')
2976
0
            CountGrep += 1;
2977
0
    }
2978
0
}
2979
2980
bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
2981
0
{
2982
0
    if (Filters.Size == 0)
2983
0
        return true;
2984
2985
0
    if (text == NULL)
2986
0
        text = text_end = "";
2987
2988
0
    for (const ImGuiTextRange& f : Filters)
2989
0
    {
2990
0
        if (f.b == f.e)
2991
0
            continue;
2992
0
        if (f.b[0] == '-')
2993
0
        {
2994
            // Subtract
2995
0
            if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)
2996
0
                return false;
2997
0
        }
2998
0
        else
2999
0
        {
3000
            // Grep
3001
0
            if (ImStristr(text, text_end, f.b, f.e) != NULL)
3002
0
                return true;
3003
0
        }
3004
0
    }
3005
3006
    // Implicit * grep
3007
0
    if (CountGrep == 0)
3008
0
        return true;
3009
3010
0
    return false;
3011
0
}
3012
3013
//-----------------------------------------------------------------------------
3014
// [SECTION] ImGuiTextBuffer, ImGuiTextIndex
3015
//-----------------------------------------------------------------------------
3016
3017
// On some platform vsnprintf() takes va_list by reference and modifies it.
3018
// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.
3019
#ifndef va_copy
3020
#if defined(__GNUC__) || defined(__clang__)
3021
#define va_copy(dest, src) __builtin_va_copy(dest, src)
3022
#else
3023
#define va_copy(dest, src) (dest = src)
3024
#endif
3025
#endif
3026
3027
char ImGuiTextBuffer::EmptyString[1] = { 0 };
3028
3029
void ImGuiTextBuffer::append(const char* str, const char* str_end)
3030
55
{
3031
55
    int len = str_end ? (int)(str_end - str) : (int)ImStrlen(str);
3032
3033
    // Add zero-terminator the first time
3034
55
    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
3035
55
    const int needed_sz = write_off + len;
3036
55
    if (write_off + len >= Buf.Capacity)
3037
0
    {
3038
0
        int new_capacity = Buf.Capacity * 2;
3039
0
        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
3040
0
    }
3041
3042
55
    Buf.resize(needed_sz);
3043
55
    memcpy(&Buf[write_off - 1], str, (size_t)len);
3044
55
    Buf[write_off - 1 + len] = 0;
3045
55
}
3046
3047
void ImGuiTextBuffer::appendf(const char* fmt, ...)
3048
113
{
3049
113
    va_list args;
3050
113
    va_start(args, fmt);
3051
113
    appendfv(fmt, args);
3052
113
    va_end(args);
3053
113
}
3054
3055
// Helper: Text buffer for logging/accumulating text
3056
void ImGuiTextBuffer::appendfv(const char* fmt, va_list args)
3057
113
{
3058
113
    va_list args_copy;
3059
113
    va_copy(args_copy, args);
3060
3061
113
    int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.
3062
113
    if (len <= 0)
3063
0
    {
3064
0
        va_end(args_copy);
3065
0
        return;
3066
0
    }
3067
3068
    // Add zero-terminator the first time
3069
113
    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;
3070
113
    const int needed_sz = write_off + len;
3071
113
    if (write_off + len >= Buf.Capacity)
3072
0
    {
3073
0
        int new_capacity = Buf.Capacity * 2;
3074
0
        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);
3075
0
    }
3076
3077
113
    Buf.resize(needed_sz);
3078
113
    ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);
3079
113
    va_end(args_copy);
3080
113
}
3081
3082
IM_MSVC_RUNTIME_CHECKS_OFF
3083
void ImGuiTextIndex::append(const char* base, int old_size, int new_size)
3084
0
{
3085
0
    IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);
3086
0
    if (old_size == new_size)
3087
0
        return;
3088
0
    if (EndOffset == 0 || base[EndOffset - 1] == '\n')
3089
0
        Offsets.push_back(EndOffset);
3090
0
    const char* base_end = base + new_size;
3091
0
    for (const char* p = base + old_size; (p = (const char*)ImMemchr(p, '\n', base_end - p)) != 0; )
3092
0
        if (++p < base_end) // Don't push a trailing offset on last \n
3093
0
            Offsets.push_back((int)(intptr_t)(p - base));
3094
0
    EndOffset = ImMax(EndOffset, new_size);
3095
0
}
3096
IM_MSVC_RUNTIME_CHECKS_RESTORE
3097
3098
//-----------------------------------------------------------------------------
3099
// [SECTION] ImGuiListClipper
3100
//-----------------------------------------------------------------------------
3101
3102
// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.
3103
// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.
3104
static bool GetSkipItemForListClipping()
3105
0
{
3106
0
    ImGuiContext& g = *GImGui;
3107
0
    return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
3108
0
}
3109
3110
static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
3111
0
{
3112
0
    if (ranges.Size - offset <= 1)
3113
0
        return;
3114
3115
    // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
3116
0
    for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
3117
0
        for (int i = offset; i < sort_end + offset; ++i)
3118
0
            if (ranges[i].Min > ranges[i + 1].Min)
3119
0
                ImSwap(ranges[i], ranges[i + 1]);
3120
3121
    // Now fuse ranges together as much as possible.
3122
0
    for (int i = 1 + offset; i < ranges.Size; i++)
3123
0
    {
3124
0
        IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
3125
0
        if (ranges[i - 1].Max < ranges[i].Min)
3126
0
            continue;
3127
0
        ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
3128
0
        ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
3129
0
        ranges.erase(ranges.Data + i);
3130
0
        i--;
3131
0
    }
3132
0
}
3133
3134
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(ImGuiListClipper* clipper, float pos_y, float line_height)
3135
0
{
3136
    // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
3137
    // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
3138
    // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?
3139
0
    ImGuiContext& g = *GImGui;
3140
0
    ImGuiWindow* window = g.CurrentWindow;
3141
0
    float off_y = pos_y - window->DC.CursorPos.y;
3142
0
    window->DC.CursorPos.y = pos_y;
3143
0
    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
3144
0
    window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;  // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
3145
0
    window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y);      // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
3146
0
    if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
3147
0
        columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly
3148
0
    if (ImGuiTable* table = g.CurrentTable)
3149
0
    {
3150
0
        if (table->IsInsideRow)
3151
0
            ImGui::TableEndRow(table);
3152
0
        const int row_increase = (int)((off_y / line_height) + 0.5f);
3153
0
        if (row_increase > 0 && (clipper->Flags & ImGuiListClipperFlags_NoSetTableRowCounters) == 0) // If your clipper item height is != from actual table row height, consider using ImGuiListClipperFlags_NoSetTableRowCounters. See #8886.
3154
0
        {
3155
0
            table->CurrentRow += row_increase;
3156
0
            table->RowBgColorCounter += row_increase;
3157
0
        }
3158
0
        table->RowPosY2 = window->DC.CursorPos.y;
3159
0
    }
3160
0
}
3161
3162
ImGuiListClipper::ImGuiListClipper()
3163
2
{
3164
2
    memset(this, 0, sizeof(*this));
3165
2
}
3166
3167
ImGuiListClipper::~ImGuiListClipper()
3168
2
{
3169
2
    End();
3170
2
}
3171
3172
void ImGuiListClipper::Begin(int items_count, float items_height)
3173
0
{
3174
0
    if (Ctx == NULL)
3175
0
        Ctx = ImGui::GetCurrentContext();
3176
3177
0
    ImGuiContext& g = *Ctx;
3178
0
    ImGuiWindow* window = g.CurrentWindow;
3179
0
    IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
3180
3181
0
    if (ImGuiTable* table = g.CurrentTable)
3182
0
        if (table->IsInsideRow)
3183
0
            ImGui::TableEndRow(table);
3184
3185
0
    StartPosY = window->DC.CursorPos.y;
3186
0
    ItemsHeight = items_height;
3187
0
    ItemsCount = items_count;
3188
0
    DisplayStart = -1;
3189
0
    DisplayEnd = 0;
3190
3191
    // Acquire temporary buffer
3192
0
    if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
3193
0
        g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
3194
0
    ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3195
0
    data->Reset(this);
3196
0
    data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
3197
0
    TempData = data;
3198
0
    StartSeekOffsetY = data->LossynessOffset;
3199
0
}
3200
3201
void ImGuiListClipper::End()
3202
2
{
3203
2
    if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
3204
0
    {
3205
        // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
3206
0
        ImGuiContext& g = *Ctx;
3207
0
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
3208
0
        if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
3209
0
            SeekCursorForItem(ItemsCount);
3210
3211
        // Restore temporary buffer and fix back pointers which may be invalidated when nesting
3212
0
        IM_ASSERT(data->ListClipper == this);
3213
0
        data->StepNo = data->Ranges.Size;
3214
0
        if (--g.ClipperTempDataStacked > 0)
3215
0
        {
3216
0
            data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
3217
0
            data->ListClipper->TempData = data;
3218
0
        }
3219
0
        TempData = NULL;
3220
0
    }
3221
2
    ItemsCount = -1;
3222
2
}
3223
3224
void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
3225
0
{
3226
0
    ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
3227
0
    IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.
3228
0
    IM_ASSERT(item_begin <= item_end);
3229
0
    if (item_begin < item_end)
3230
0
        data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
3231
0
}
3232
3233
// This is already called while stepping.
3234
// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.
3235
void ImGuiListClipper::SeekCursorForItem(int item_n)
3236
0
{
3237
    // - Perform the add and multiply with double to allow seeking through larger ranges.
3238
    // - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
3239
    // - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
3240
0
    float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
3241
0
    ImGuiListClipper_SeekCursorAndSetupPrevLine(this, pos_y, ItemsHeight);
3242
0
}
3243
3244
static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
3245
0
{
3246
0
    ImGuiContext& g = *clipper->Ctx;
3247
0
    ImGuiWindow* window = g.CurrentWindow;
3248
0
    ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
3249
0
    IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?");
3250
3251
0
    ImGuiTable* table = g.CurrentTable;
3252
0
    if (table && table->IsInsideRow)
3253
0
        ImGui::TableEndRow(table);
3254
3255
    // No items
3256
0
    if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())
3257
0
        return false;
3258
3259
    // While we are in frozen row state, keep displaying items one by one, unclipped
3260
    // FIXME: Could be stored as a table-agnostic state.
3261
0
    if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
3262
0
    {
3263
0
        clipper->DisplayStart = data->ItemsFrozen;
3264
0
        clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);
3265
0
        if (clipper->DisplayStart < clipper->DisplayEnd)
3266
0
            data->ItemsFrozen++;
3267
0
        return true;
3268
0
    }
3269
3270
    // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
3271
0
    bool calc_clipping = false;
3272
0
    if (data->StepNo == 0)
3273
0
    {
3274
0
        clipper->StartPosY = window->DC.CursorPos.y;
3275
0
        if (clipper->ItemsHeight <= 0.0f)
3276
0
        {
3277
            // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
3278
0
            data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
3279
0
            clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
3280
0
            clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);
3281
0
            data->StepNo = 1;
3282
0
            return true;
3283
0
        }
3284
0
        calc_clipping = true;   // If on the first step with known item height, calculate clipping.
3285
0
    }
3286
3287
    // Step 1: Let the clipper infer height from first range
3288
0
    if (clipper->ItemsHeight <= 0.0f)
3289
0
    {
3290
0
        IM_ASSERT(data->StepNo == 1);
3291
0
        if (table)
3292
0
            IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);
3293
3294
0
        bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);
3295
0
        if (affected_by_floating_point_precision)
3296
0
        {
3297
            // Mitigation/hack for very large range: assume last time height constitute line height.
3298
0
            clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.
3299
0
            window->DC.CursorPos.y = (float)(clipper->StartPosY + clipper->ItemsHeight);
3300
0
        }
3301
0
        else
3302
0
        {
3303
0
            clipper->ItemsHeight = (float)(window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);
3304
0
        }
3305
0
        if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode.
3306
0
            return false;
3307
0
        IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
3308
0
        calc_clipping = true;   // If item height had to be calculated, calculate clipping afterwards.
3309
0
    }
3310
3311
    // Step 0 or 1: Calculate the actual ranges of visible elements.
3312
0
    const int already_submitted = clipper->DisplayEnd;
3313
0
    if (calc_clipping)
3314
0
    {
3315
        // Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
3316
0
        clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;
3317
3318
0
        if (g.LogEnabled)
3319
0
        {
3320
            // If logging is active, do not perform any clipping
3321
0
            data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));
3322
0
        }
3323
0
        else
3324
0
        {
3325
            // Add range selected to be included for navigation
3326
0
            const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
3327
0
            if (is_nav_request)
3328
0
            {
3329
0
                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringRect.Min.y, g.NavScoringRect.Max.y, 0, 0));
3330
0
                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
3331
0
            }
3332
0
            if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)
3333
0
                data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));
3334
3335
            // Add focused/active item
3336
0
            ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
3337
0
            if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
3338
0
                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
3339
3340
0
            float min_y = window->ClipRect.Min.y;
3341
0
            float max_y = window->ClipRect.Max.y;
3342
3343
            // Add box selection range
3344
0
            ImGuiBoxSelectState* bs = &g.BoxSelectState;
3345
0
            if (bs->IsActive && bs->Window == window)
3346
0
            {
3347
                // FIXME: Selectable() use of half-ItemSpacing isn't consistent in matter of layout, as ItemAdd(bb) stray above ItemSize()'s CursorPos.
3348
                // RangeSelect's BoxSelect relies on comparing overlap of previous and current rectangle and is sensitive to that.
3349
                // As a workaround we currently half ItemSpacing worth on each side.
3350
0
                min_y -= g.Style.ItemSpacing.y;
3351
0
                max_y += g.Style.ItemSpacing.y;
3352
3353
                // Box-select on 2D area requires different clipping.
3354
0
                if (bs->UnclipMode)
3355
0
                    data->Ranges.push_back(ImGuiListClipperRange::FromPositions(bs->UnclipRect.Min.y, bs->UnclipRect.Max.y, 0, 0));
3356
0
            }
3357
3358
            // Add main visible range
3359
0
            const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
3360
0
            const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
3361
0
            data->Ranges.push_back(ImGuiListClipperRange::FromPositions(min_y, max_y, off_min, off_max));
3362
0
        }
3363
3364
        // Convert position ranges to item index ranges
3365
        // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
3366
        // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
3367
        //   which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
3368
0
        for (ImGuiListClipperRange& range : data->Ranges)
3369
0
            if (range.PosToIndexConvert)
3370
0
            {
3371
0
                int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);
3372
0
                int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);
3373
0
                range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);
3374
0
                range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);
3375
0
                range.PosToIndexConvert = false;
3376
0
            }
3377
0
        ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
3378
0
    }
3379
3380
    // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
3381
0
    while (data->StepNo < data->Ranges.Size)
3382
0
    {
3383
0
        clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
3384
0
        clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
3385
0
        data->StepNo++;
3386
0
        if (clipper->DisplayStart >= clipper->DisplayEnd)
3387
0
            continue;
3388
0
        if (clipper->DisplayStart > already_submitted)
3389
0
            clipper->SeekCursorForItem(clipper->DisplayStart);
3390
0
        return true;
3391
0
    }
3392
3393
    // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
3394
    // Advance the cursor to the end of the list and then returns 'false' to end the loop.
3395
0
    if (clipper->ItemsCount < INT_MAX)
3396
0
        clipper->SeekCursorForItem(clipper->ItemsCount);
3397
3398
0
    return false;
3399
0
}
3400
3401
bool ImGuiListClipper::Step()
3402
0
{
3403
0
    ImGuiContext& g = *Ctx;
3404
0
    bool need_items_height = (ItemsHeight <= 0.0f);
3405
0
    bool ret = ImGuiListClipper_StepInternal(this);
3406
0
    if (ret && (DisplayStart >= DisplayEnd))
3407
0
        ret = false;
3408
0
    if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)
3409
0
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n");
3410
0
    if (need_items_height && ItemsHeight > 0.0f)
3411
0
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight);
3412
0
    if (ret)
3413
0
    {
3414
0
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd);
3415
0
    }
3416
0
    else
3417
0
    {
3418
0
        IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n");
3419
0
        End();
3420
0
    }
3421
0
    return ret;
3422
0
}
3423
3424
// Generic helper, equivalent to old ImGui::CalcListClipping() but statelesss
3425
void ImGui::CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end)
3426
0
{
3427
0
    *out_visible_start = ImMax((int)((clip_rect.Min.y - pos.y) / items_height), 0);
3428
0
    *out_visible_end = ImMax((int)ImCeil((clip_rect.Max.y - pos.y) / items_height), *out_visible_start);
3429
0
}
3430
3431
//-----------------------------------------------------------------------------
3432
// [SECTION] STYLING
3433
//-----------------------------------------------------------------------------
3434
3435
ImGuiStyle& ImGui::GetStyle()
3436
1
{
3437
1
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
3438
1
    return GImGui->Style;
3439
1
}
3440
3441
ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)
3442
972k
{
3443
972k
    ImGuiStyle& style = GImGui->Style;
3444
972k
    ImVec4 c = style.Colors[idx];
3445
972k
    c.w *= style.Alpha * alpha_mul;
3446
972k
    return ColorConvertFloat4ToU32(c);
3447
972k
}
3448
3449
ImU32 ImGui::GetColorU32(const ImVec4& col)
3450
0
{
3451
0
    ImGuiStyle& style = GImGui->Style;
3452
0
    ImVec4 c = col;
3453
0
    c.w *= style.Alpha;
3454
0
    return ColorConvertFloat4ToU32(c);
3455
0
}
3456
3457
const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)
3458
0
{
3459
0
    ImGuiStyle& style = GImGui->Style;
3460
0
    return style.Colors[idx];
3461
0
}
3462
3463
ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul)
3464
0
{
3465
0
    ImGuiStyle& style = GImGui->Style;
3466
0
    alpha_mul *= style.Alpha;
3467
0
    if (alpha_mul >= 1.0f)
3468
0
        return col;
3469
0
    ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;
3470
0
    a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range.
3471
0
    return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);
3472
0
}
3473
3474
// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32
3475
void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)
3476
0
{
3477
0
    ImGuiContext& g = *GImGui;
3478
0
    ImGuiColorMod backup;
3479
0
    backup.Col = idx;
3480
0
    backup.BackupValue = g.Style.Colors[idx];
3481
0
    g.ColorStack.push_back(backup);
3482
0
    if (g.DebugFlashStyleColorIdx != idx)
3483
0
        g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);
3484
0
}
3485
3486
void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)
3487
0
{
3488
0
    ImGuiContext& g = *GImGui;
3489
0
    ImGuiColorMod backup;
3490
0
    backup.Col = idx;
3491
0
    backup.BackupValue = g.Style.Colors[idx];
3492
0
    g.ColorStack.push_back(backup);
3493
0
    if (g.DebugFlashStyleColorIdx != idx)
3494
0
        g.Style.Colors[idx] = col;
3495
0
}
3496
3497
void ImGui::PopStyleColor(int count)
3498
0
{
3499
0
    ImGuiContext& g = *GImGui;
3500
0
    if (g.ColorStack.Size < count)
3501
0
    {
3502
0
        IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!");
3503
0
        count = g.ColorStack.Size;
3504
0
    }
3505
0
    while (count > 0)
3506
0
    {
3507
0
        ImGuiColorMod& backup = g.ColorStack.back();
3508
0
        g.Style.Colors[backup.Col] = backup.BackupValue;
3509
0
        g.ColorStack.pop_back();
3510
0
        count--;
3511
0
    }
3512
0
}
3513
3514
static const ImGuiStyleVarInfo GStyleVarsInfo[] =
3515
{
3516
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, Alpha) },                     // ImGuiStyleVar_Alpha
3517
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) },             // ImGuiStyleVar_DisabledAlpha
3518
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowPadding) },             // ImGuiStyleVar_WindowPadding
3519
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowRounding) },            // ImGuiStyleVar_WindowRounding
3520
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) },          // ImGuiStyleVar_WindowBorderSize
3521
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowMinSize) },             // ImGuiStyleVar_WindowMinSize
3522
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) },          // ImGuiStyleVar_WindowTitleAlign
3523
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildRounding) },             // ImGuiStyleVar_ChildRounding
3524
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) },           // ImGuiStyleVar_ChildBorderSize
3525
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupRounding) },             // ImGuiStyleVar_PopupRounding
3526
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) },           // ImGuiStyleVar_PopupBorderSize
3527
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FramePadding) },              // ImGuiStyleVar_FramePadding
3528
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameRounding) },             // ImGuiStyleVar_FrameRounding
3529
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) },           // ImGuiStyleVar_FrameBorderSize
3530
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemSpacing) },               // ImGuiStyleVar_ItemSpacing
3531
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) },          // ImGuiStyleVar_ItemInnerSpacing
3532
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, IndentSpacing) },             // ImGuiStyleVar_IndentSpacing
3533
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, CellPadding) },               // ImGuiStyleVar_CellPadding
3534
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) },             // ImGuiStyleVar_ScrollbarSize
3535
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) },         // ImGuiStyleVar_ScrollbarRounding
3536
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ScrollbarPadding) },          // ImGuiStyleVar_ScrollbarPadding
3537
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabMinSize) },               // ImGuiStyleVar_GrabMinSize
3538
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, GrabRounding) },              // ImGuiStyleVar_GrabRounding
3539
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ImageBorderSize) },           // ImGuiStyleVar_ImageBorderSize
3540
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabRounding) },               // ImGuiStyleVar_TabRounding
3541
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBorderSize) },             // ImGuiStyleVar_TabBorderSize
3542
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthBase) },           // ImGuiStyleVar_TabMinWidthBase
3543
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabMinWidthShrink) },         // ImGuiStyleVar_TabMinWidthShrink
3544
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) },          // ImGuiStyleVar_TabBarBorderSize
3545
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TabBarOverlineSize) },        // ImGuiStyleVar_TabBarOverlineSize
3546
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersAngle)},    // ImGuiStyleVar_TableAngledHeadersAngle
3547
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TableAngledHeadersTextAlign)},// ImGuiStyleVar_TableAngledHeadersTextAlign
3548
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesSize)},              // ImGuiStyleVar_TreeLinesSize
3549
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, TreeLinesRounding)},          // ImGuiStyleVar_TreeLinesRounding
3550
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) },           // ImGuiStyleVar_ButtonTextAlign
3551
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) },       // ImGuiStyleVar_SelectableTextAlign
3552
    { 1, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},    // ImGuiStyleVar_SeparatorTextBorderSize
3553
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) },        // ImGuiStyleVar_SeparatorTextAlign
3554
    { 2, ImGuiDataType_Float, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) },      // ImGuiStyleVar_SeparatorTextPadding
3555
};
3556
3557
const ImGuiStyleVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
3558
807k
{
3559
807k
    IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);
3560
807k
    IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarsInfo) == ImGuiStyleVar_COUNT);
3561
807k
    return &GStyleVarsInfo[idx];
3562
807k
}
3563
3564
void ImGui::PushStyleVar(ImGuiStyleVar idx, float val)
3565
81.2k
{
3566
81.2k
    ImGuiContext& g = *GImGui;
3567
81.2k
    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3568
81.2k
    if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 1)
3569
0
    {
3570
0
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3571
0
        return;
3572
0
    }
3573
81.2k
    float* pvar = (float*)var_info->GetVarPtr(&g.Style);
3574
81.2k
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3575
81.2k
    *pvar = val;
3576
81.2k
}
3577
3578
void ImGui::PushStyleVarX(ImGuiStyleVar idx, float val_x)
3579
241k
{
3580
241k
    ImGuiContext& g = *GImGui;
3581
241k
    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3582
241k
    if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3583
0
    {
3584
0
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3585
0
        return;
3586
0
    }
3587
241k
    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3588
241k
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3589
241k
    pvar->x = val_x;
3590
241k
}
3591
3592
void ImGui::PushStyleVarY(ImGuiStyleVar idx, float val_y)
3593
0
{
3594
0
    ImGuiContext& g = *GImGui;
3595
0
    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3596
0
    if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3597
0
    {
3598
0
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3599
0
        return;
3600
0
    }
3601
0
    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3602
0
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3603
0
    pvar->y = val_y;
3604
0
}
3605
3606
void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)
3607
80.5k
{
3608
80.5k
    ImGuiContext& g = *GImGui;
3609
80.5k
    const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx);
3610
80.5k
    if (var_info->DataType != ImGuiDataType_Float || var_info->Count != 2)
3611
0
    {
3612
0
        IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!");
3613
0
        return;
3614
0
    }
3615
80.5k
    ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);
3616
80.5k
    g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));
3617
80.5k
    *pvar = val;
3618
80.5k
}
3619
3620
void ImGui::PopStyleVar(int count)
3621
323k
{
3622
323k
    ImGuiContext& g = *GImGui;
3623
323k
    if (g.StyleVarStack.Size < count)
3624
0
    {
3625
0
        IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!");
3626
0
        count = g.StyleVarStack.Size;
3627
0
    }
3628
726k
    while (count > 0)
3629
403k
    {
3630
        // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.
3631
403k
        ImGuiStyleMod& backup = g.StyleVarStack.back();
3632
403k
        const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(backup.VarIdx);
3633
403k
        void* data = var_info->GetVarPtr(&g.Style);
3634
403k
        if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }
3635
322k
        else if (var_info->DataType == ImGuiDataType_Float && var_info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }
3636
403k
        g.StyleVarStack.pop_back();
3637
403k
        count--;
3638
403k
    }
3639
323k
}
3640
3641
const char* ImGui::GetStyleColorName(ImGuiCol idx)
3642
0
{
3643
    // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1";
3644
0
    switch (idx)
3645
0
    {
3646
0
    case ImGuiCol_Text: return "Text";
3647
0
    case ImGuiCol_TextDisabled: return "TextDisabled";
3648
0
    case ImGuiCol_WindowBg: return "WindowBg";
3649
0
    case ImGuiCol_ChildBg: return "ChildBg";
3650
0
    case ImGuiCol_PopupBg: return "PopupBg";
3651
0
    case ImGuiCol_Border: return "Border";
3652
0
    case ImGuiCol_BorderShadow: return "BorderShadow";
3653
0
    case ImGuiCol_FrameBg: return "FrameBg";
3654
0
    case ImGuiCol_FrameBgHovered: return "FrameBgHovered";
3655
0
    case ImGuiCol_FrameBgActive: return "FrameBgActive";
3656
0
    case ImGuiCol_TitleBg: return "TitleBg";
3657
0
    case ImGuiCol_TitleBgActive: return "TitleBgActive";
3658
0
    case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed";
3659
0
    case ImGuiCol_MenuBarBg: return "MenuBarBg";
3660
0
    case ImGuiCol_ScrollbarBg: return "ScrollbarBg";
3661
0
    case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab";
3662
0
    case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered";
3663
0
    case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive";
3664
0
    case ImGuiCol_CheckMark: return "CheckMark";
3665
0
    case ImGuiCol_SliderGrab: return "SliderGrab";
3666
0
    case ImGuiCol_SliderGrabActive: return "SliderGrabActive";
3667
0
    case ImGuiCol_Button: return "Button";
3668
0
    case ImGuiCol_ButtonHovered: return "ButtonHovered";
3669
0
    case ImGuiCol_ButtonActive: return "ButtonActive";
3670
0
    case ImGuiCol_Header: return "Header";
3671
0
    case ImGuiCol_HeaderHovered: return "HeaderHovered";
3672
0
    case ImGuiCol_HeaderActive: return "HeaderActive";
3673
0
    case ImGuiCol_Separator: return "Separator";
3674
0
    case ImGuiCol_SeparatorHovered: return "SeparatorHovered";
3675
0
    case ImGuiCol_SeparatorActive: return "SeparatorActive";
3676
0
    case ImGuiCol_ResizeGrip: return "ResizeGrip";
3677
0
    case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered";
3678
0
    case ImGuiCol_ResizeGripActive: return "ResizeGripActive";
3679
0
    case ImGuiCol_InputTextCursor: return "InputTextCursor";
3680
0
    case ImGuiCol_TabHovered: return "TabHovered";
3681
0
    case ImGuiCol_Tab: return "Tab";
3682
0
    case ImGuiCol_TabSelected: return "TabSelected";
3683
0
    case ImGuiCol_TabSelectedOverline: return "TabSelectedOverline";
3684
0
    case ImGuiCol_TabDimmed: return "TabDimmed";
3685
0
    case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected";
3686
0
    case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline";
3687
0
    case ImGuiCol_PlotLines: return "PlotLines";
3688
0
    case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
3689
0
    case ImGuiCol_PlotHistogram: return "PlotHistogram";
3690
0
    case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered";
3691
0
    case ImGuiCol_TableHeaderBg: return "TableHeaderBg";
3692
0
    case ImGuiCol_TableBorderStrong: return "TableBorderStrong";
3693
0
    case ImGuiCol_TableBorderLight: return "TableBorderLight";
3694
0
    case ImGuiCol_TableRowBg: return "TableRowBg";
3695
0
    case ImGuiCol_TableRowBgAlt: return "TableRowBgAlt";
3696
0
    case ImGuiCol_TextLink: return "TextLink";
3697
0
    case ImGuiCol_TextSelectedBg: return "TextSelectedBg";
3698
0
    case ImGuiCol_TreeLines: return "TreeLines";
3699
0
    case ImGuiCol_DragDropTarget: return "DragDropTarget";
3700
0
    case ImGuiCol_UnsavedMarker: return "UnsavedMarker";
3701
0
    case ImGuiCol_NavCursor: return "NavCursor";
3702
0
    case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight";
3703
0
    case ImGuiCol_NavWindowingDimBg: return "NavWindowingDimBg";
3704
0
    case ImGuiCol_ModalWindowDimBg: return "ModalWindowDimBg";
3705
0
    }
3706
0
    IM_ASSERT(0);
3707
0
    return "Unknown";
3708
0
}
3709
3710
//-----------------------------------------------------------------------------
3711
// [SECTION] RENDER HELPERS
3712
// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,
3713
// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.
3714
// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.
3715
//-----------------------------------------------------------------------------
3716
3717
const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)
3718
1.13M
{
3719
1.13M
    const char* text_display_end = text;
3720
1.13M
    if (!text_end)
3721
1.13M
        text_end = (const char*)-1;
3722
3723
4.22M
    while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))
3724
3.08M
        text_display_end++;
3725
1.13M
    return text_display_end;
3726
1.13M
}
3727
3728
// Internal ImGui functions to render text
3729
// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()
3730
void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
3731
243k
{
3732
243k
    ImGuiContext& g = *GImGui;
3733
243k
    ImGuiWindow* window = g.CurrentWindow;
3734
3735
    // Hide anything after a '##' string
3736
243k
    const char* text_display_end;
3737
243k
    if (hide_text_after_hash)
3738
243k
    {
3739
243k
        text_display_end = FindRenderedTextEnd(text, text_end);
3740
243k
    }
3741
0
    else
3742
0
    {
3743
0
        if (!text_end)
3744
0
            text_end = text + ImStrlen(text); // FIXME-OPT
3745
0
        text_display_end = text_end;
3746
0
    }
3747
3748
243k
    if (text != text_display_end)
3749
243k
    {
3750
243k
        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
3751
243k
        if (g.LogEnabled)
3752
0
            LogRenderedText(&pos, text, text_display_end);
3753
243k
    }
3754
243k
}
3755
3756
void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
3757
0
{
3758
0
    ImGuiContext& g = *GImGui;
3759
0
    ImGuiWindow* window = g.CurrentWindow;
3760
3761
0
    if (!text_end)
3762
0
        text_end = text + ImStrlen(text); // FIXME-OPT
3763
3764
0
    if (text != text_end)
3765
0
    {
3766
0
        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
3767
0
        if (g.LogEnabled)
3768
0
            LogRenderedText(&pos, text, text_end);
3769
0
    }
3770
0
}
3771
3772
// Default clip_rect uses (pos_min,pos_max)
3773
// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)
3774
// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especially for text above draw_list->DrawList.
3775
// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take
3776
// better advantage of the render function taking size into account for coarse clipping.
3777
void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
3778
80.5k
{
3779
    // Perform CPU side clipping for single clipped element to avoid using scissor state
3780
80.5k
    ImVec2 pos = pos_min;
3781
80.5k
    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);
3782
3783
80.5k
    const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;
3784
80.5k
    const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;
3785
80.5k
    bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);
3786
80.5k
    if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min
3787
80.5k
        need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);
3788
3789
    // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.
3790
80.5k
    if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);
3791
80.5k
    if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);
3792
3793
    // Render
3794
80.5k
    if (need_clipping)
3795
0
    {
3796
0
        ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);
3797
0
        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);
3798
0
    }
3799
80.5k
    else
3800
80.5k
    {
3801
80.5k
        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);
3802
80.5k
    }
3803
80.5k
}
3804
3805
void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)
3806
324k
{
3807
    // Hide anything after a '##' string
3808
324k
    const char* text_display_end = FindRenderedTextEnd(text, text_end);
3809
324k
    const int text_len = (int)(text_display_end - text);
3810
324k
    if (text_len == 0)
3811
243k
        return;
3812
3813
80.5k
    ImGuiContext& g = *GImGui;
3814
80.5k
    ImGuiWindow* window = g.CurrentWindow;
3815
80.5k
    RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);
3816
80.5k
    if (g.LogEnabled)
3817
0
        LogRenderedText(&pos_min, text, text_display_end);
3818
80.5k
}
3819
3820
// Another overly complex function until we reorganize everything into a nice all-in-one helper.
3821
// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.
3822
// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
3823
// (BREAKING) On 2025/04/16 we removed the 'float clip_max_x' parameters which was preceding 'float ellipsis_max' and was the same value for 99% of users.
3824
void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
3825
0
{
3826
0
    ImGuiContext& g = *GImGui;
3827
0
    if (text_end_full == NULL)
3828
0
        text_end_full = FindRenderedTextEnd(text);
3829
0
    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
3830
3831
    //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));
3832
    //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));
3833
3834
    // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
3835
0
    if (text_size.x > pos_max.x - pos_min.x)
3836
0
    {
3837
        // Hello wo...
3838
        // |       |   |
3839
        // min   max   ellipsis_max
3840
        //          <-> this is generally some padding value
3841
3842
0
        ImFont* font = draw_list->_Data->Font;
3843
0
        const float font_size = draw_list->_Data->FontSize;
3844
0
        const float font_scale = draw_list->_Data->FontScale;
3845
0
        const char* text_end_ellipsis = NULL;
3846
0
        ImFontBaked* baked = font->GetFontBaked(font_size);
3847
0
        const float ellipsis_width = baked->GetCharAdvance(font->EllipsisChar) * font_scale;
3848
3849
        // We can now claim the space between pos_max.x and ellipsis_max.x
3850
0
        const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);
3851
0
        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
3852
0
        while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
3853
0
        {
3854
            // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
3855
0
            text_end_ellipsis--;
3856
0
            text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
3857
0
        }
3858
3859
        // Render text, render ellipsis
3860
0
        RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
3861
0
        ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);
3862
0
        ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
3863
0
        font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);
3864
0
    }
3865
0
    else
3866
0
    {
3867
0
        RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
3868
0
    }
3869
3870
0
    if (g.LogEnabled)
3871
0
        LogRenderedText(&pos_min, text, text_end_full);
3872
0
}
3873
3874
// Render a rectangle shaped with optional rounding and borders
3875
void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders, float rounding)
3876
1.58k
{
3877
1.58k
    ImGuiContext& g = *GImGui;
3878
1.58k
    ImGuiWindow* window = g.CurrentWindow;
3879
1.58k
    window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);
3880
1.58k
    const float border_size = g.Style.FrameBorderSize;
3881
1.58k
    if (borders && border_size > 0.0f)
3882
0
    {
3883
0
        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3884
0
        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3885
0
    }
3886
1.58k
}
3887
3888
void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)
3889
0
{
3890
0
    ImGuiContext& g = *GImGui;
3891
0
    ImGuiWindow* window = g.CurrentWindow;
3892
0
    const float border_size = g.Style.FrameBorderSize;
3893
0
    if (border_size > 0.0f)
3894
0
    {
3895
0
        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);
3896
0
        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);
3897
0
    }
3898
0
}
3899
3900
void ImGui::RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags)
3901
81.2k
{
3902
81.2k
    ImGuiContext& g = *GImGui;
3903
81.2k
    if (id != g.NavId)
3904
80.5k
        return;
3905
661
    if (!g.NavCursorVisible && !(flags & ImGuiNavRenderCursorFlags_AlwaysDraw))
3906
661
        return;
3907
0
    if (id == g.LastItemData.ID && (g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
3908
0
        return;
3909
0
    ImGuiWindow* window = g.CurrentWindow;
3910
0
    if (window->DC.NavHideHighlightOneFrame)
3911
0
        return;
3912
3913
0
    float rounding = (flags & ImGuiNavRenderCursorFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;
3914
0
    ImRect display_rect = bb;
3915
0
    display_rect.ClipWith(window->ClipRect);
3916
0
    const float thickness = 2.0f;
3917
0
    if (flags & ImGuiNavRenderCursorFlags_Compact)
3918
0
    {
3919
0
        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3920
0
    }
3921
0
    else
3922
0
    {
3923
0
        const float distance = 3.0f + thickness * 0.5f;
3924
0
        display_rect.Expand(ImVec2(distance, distance));
3925
0
        bool fully_visible = window->ClipRect.Contains(display_rect);
3926
0
        if (!fully_visible)
3927
0
            window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);
3928
0
        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavCursor), rounding, 0, thickness);
3929
0
        if (!fully_visible)
3930
0
            window->DrawList->PopClipRect();
3931
0
    }
3932
0
}
3933
3934
void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)
3935
0
{
3936
0
    ImGuiContext& g = *GImGui;
3937
0
    if (mouse_cursor <= ImGuiMouseCursor_None || mouse_cursor >= ImGuiMouseCursor_COUNT) // We intentionally accept out of bound values.
3938
0
        mouse_cursor = ImGuiMouseCursor_Arrow;
3939
0
    ImFontAtlas* font_atlas = g.DrawListSharedData.FontAtlas;
3940
0
    for (ImGuiViewportP* viewport : g.Viewports)
3941
0
    {
3942
        // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.
3943
0
        ImVec2 offset, size, uv[4];
3944
0
        if (!ImFontAtlasGetMouseCursorTexData(font_atlas, mouse_cursor, &offset, &size, &uv[0], &uv[2]))
3945
0
            continue;
3946
0
        const ImVec2 pos = base_pos - offset;
3947
0
        const float scale = base_scale;
3948
0
        if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))
3949
0
            continue;
3950
0
        ImDrawList* draw_list = GetForegroundDrawList(viewport);
3951
0
        ImTextureRef tex_ref = font_atlas->TexRef;
3952
0
        draw_list->PushTexture(tex_ref);
3953
0
        draw_list->AddImage(tex_ref, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);
3954
0
        draw_list->AddImage(tex_ref, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);
3955
0
        draw_list->AddImage(tex_ref, pos,                        pos + size * scale,                  uv[2], uv[3], col_border);
3956
0
        draw_list->AddImage(tex_ref, pos,                        pos + size * scale,                  uv[0], uv[1], col_fill);
3957
0
        if (mouse_cursor == ImGuiMouseCursor_Wait || mouse_cursor == ImGuiMouseCursor_Progress)
3958
0
        {
3959
0
            float a_min = ImFmod((float)g.Time * 5.0f, 2.0f * IM_PI);
3960
0
            float a_max = a_min + IM_PI * 1.65f;
3961
0
            draw_list->PathArcTo(pos + ImVec2(14, -1) * scale, 6.0f * scale, a_min, a_max);
3962
0
            draw_list->PathStroke(col_fill, ImDrawFlags_None, 3.0f * scale);
3963
0
        }
3964
0
        draw_list->PopTexture();
3965
0
    }
3966
0
}
3967
3968
//-----------------------------------------------------------------------------
3969
// [SECTION] INITIALIZATION, SHUTDOWN
3970
//-----------------------------------------------------------------------------
3971
3972
// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself
3973
// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module
3974
ImGuiContext* ImGui::GetCurrentContext()
3975
486k
{
3976
486k
    return GImGui;
3977
486k
}
3978
3979
void ImGui::SetCurrentContext(ImGuiContext* ctx)
3980
3
{
3981
#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC
3982
    IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.
3983
#else
3984
3
    GImGui = ctx;
3985
3
#endif
3986
3
}
3987
3988
void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)
3989
0
{
3990
0
    GImAllocatorAllocFunc = alloc_func;
3991
0
    GImAllocatorFreeFunc = free_func;
3992
0
    GImAllocatorUserData = user_data;
3993
0
}
3994
3995
// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)
3996
void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)
3997
0
{
3998
0
    *p_alloc_func = GImAllocatorAllocFunc;
3999
0
    *p_free_func = GImAllocatorFreeFunc;
4000
0
    *p_user_data = GImAllocatorUserData;
4001
0
}
4002
4003
ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)
4004
1
{
4005
1
    ImGuiContext* prev_ctx = GetCurrentContext();
4006
1
    ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);
4007
1
    SetCurrentContext(ctx);
4008
1
    Initialize();
4009
1
    if (prev_ctx != NULL)
4010
0
        SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.
4011
1
    return ctx;
4012
1
}
4013
4014
void ImGui::DestroyContext(ImGuiContext* ctx)
4015
1
{
4016
1
    ImGuiContext* prev_ctx = GetCurrentContext();
4017
1
    if (ctx == NULL) //-V1051
4018
1
        ctx = prev_ctx;
4019
1
    SetCurrentContext(ctx);
4020
1
    Shutdown();
4021
1
    SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);
4022
1
    IM_DELETE(ctx);
4023
1
}
4024
4025
// IMPORTANT: interactive elements requires a fixed ###xxx suffix, it must be same in ALL languages to allow for automation.
4026
static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
4027
{
4028
    { ImGuiLocKey_VersionStr,           "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
4029
    { ImGuiLocKey_TableSizeOne,         "Size column to fit###SizeOne"          },
4030
    { ImGuiLocKey_TableSizeAllFit,      "Size all columns to fit###SizeAll"     },
4031
    { ImGuiLocKey_TableSizeAllDefault,  "Size all columns to default###SizeAll" },
4032
    { ImGuiLocKey_TableResetOrder,      "Reset order###ResetOrder"              },
4033
    { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)"                       },
4034
    { ImGuiLocKey_WindowingPopup,       "(Popup)"                               },
4035
    { ImGuiLocKey_WindowingUntitled,    "(Untitled)"                            },
4036
    { ImGuiLocKey_OpenLink_s,           "Open '%s'"                             },
4037
    { ImGuiLocKey_CopyLink,             "Copy Link###CopyLink"                  },
4038
};
4039
4040
ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas)
4041
1
{
4042
1
    IO.Ctx = this;
4043
1
    InputTextState.Ctx = this;
4044
4045
1
    Initialized = false;
4046
1
    Font = NULL;
4047
1
    FontBaked = NULL;
4048
1
    FontSize = FontSizeBase = FontBakedScale = CurrentDpiScale = 0.0f;
4049
1
    FontRasterizerDensity = 1.0f;
4050
1
    IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();
4051
1
    if (shared_font_atlas == NULL)
4052
1
        IO.Fonts->OwnerContext = this;
4053
1
    Time = 0.0f;
4054
1
    FrameCount = 0;
4055
1
    FrameCountEnded = FrameCountRendered = -1;
4056
1
    WithinEndChildID = 0;
4057
1
    WithinFrameScope = WithinFrameScopeWithImplicitWindow = false;
4058
1
    GcCompactAll = false;
4059
1
    TestEngineHookItems = false;
4060
1
    TestEngine = NULL;
4061
1
    memset(ContextName, 0, sizeof(ContextName));
4062
4063
1
    InputEventsNextMouseSource = ImGuiMouseSource_Mouse;
4064
1
    InputEventsNextEventId = 1;
4065
4066
1
    WindowsActiveCount = 0;
4067
1
    WindowsBorderHoverPadding = 0.0f;
4068
1
    CurrentWindow = NULL;
4069
1
    HoveredWindow = NULL;
4070
1
    HoveredWindowUnderMovingWindow = NULL;
4071
1
    HoveredWindowBeforeClear = NULL;
4072
1
    MovingWindow = NULL;
4073
1
    WheelingWindow = NULL;
4074
1
    WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;
4075
1
    WheelingWindowReleaseTimer = 0.0f;
4076
4077
1
    DebugDrawIdConflictsId = 0;
4078
1
    DebugHookIdInfoId = 0;
4079
1
    HoveredId = HoveredIdPreviousFrame = 0;
4080
1
    HoveredIdPreviousFrameItemCount = 0;
4081
1
    HoveredIdAllowOverlap = false;
4082
1
    HoveredIdIsDisabled = false;
4083
1
    HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;
4084
1
    ItemUnclipByLog = false;
4085
1
    ActiveId = 0;
4086
1
    ActiveIdIsAlive = 0;
4087
1
    ActiveIdTimer = 0.0f;
4088
1
    ActiveIdIsJustActivated = false;
4089
1
    ActiveIdAllowOverlap = false;
4090
1
    ActiveIdNoClearOnFocusLoss = false;
4091
1
    ActiveIdHasBeenPressedBefore = false;
4092
1
    ActiveIdHasBeenEditedBefore = false;
4093
1
    ActiveIdHasBeenEditedThisFrame = false;
4094
1
    ActiveIdFromShortcut = false;
4095
1
    ActiveIdClickOffset = ImVec2(-1, -1);
4096
1
    ActiveIdWindow = NULL;
4097
1
    ActiveIdSource = ImGuiInputSource_None;
4098
1
    ActiveIdDisabledId = 0;
4099
1
    ActiveIdMouseButton = -1;
4100
1
    ActiveIdPreviousFrame = 0;
4101
1
    memset(&DeactivatedItemData, 0, sizeof(DeactivatedItemData));
4102
1
    memset(&ActiveIdValueOnActivation, 0, sizeof(ActiveIdValueOnActivation));
4103
1
    LastActiveId = 0;
4104
1
    LastActiveIdTimer = 0.0f;
4105
4106
1
    LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0;
4107
4108
1
    ActiveIdUsingNavDirMask = 0x00;
4109
1
    ActiveIdUsingAllKeyboardKeys = false;
4110
4111
1
    CurrentFocusScopeId = 0;
4112
1
    CurrentItemFlags = ImGuiItemFlags_None;
4113
1
    DebugShowGroupRects = false;
4114
4115
1
    NavCursorVisible = false;
4116
1
    NavHighlightItemUnderNav = false;
4117
1
    NavMousePosDirty = false;
4118
1
    NavIdIsAlive = false;
4119
1
    NavId = 0;
4120
1
    NavWindow = NULL;
4121
1
    NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;
4122
1
    NavLayer = ImGuiNavLayer_Main;
4123
1
    NavNextActivateId = 0;
4124
1
    NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;
4125
1
    NavHighlightActivatedId = 0;
4126
1
    NavHighlightActivatedTimer = 0.0f;
4127
1
    NavInputSource = ImGuiInputSource_Keyboard;
4128
1
    NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
4129
1
    NavCursorHideFrames = 0;
4130
4131
1
    NavAnyRequest = false;
4132
1
    NavInitRequest = false;
4133
1
    NavInitRequestFromMove = false;
4134
1
    NavMoveSubmitted = false;
4135
1
    NavMoveScoringItems = false;
4136
1
    NavMoveForwardToNextFrame = false;
4137
1
    NavMoveFlags = ImGuiNavMoveFlags_None;
4138
1
    NavMoveScrollFlags = ImGuiScrollFlags_None;
4139
1
    NavMoveKeyMods = ImGuiMod_None;
4140
1
    NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;
4141
1
    NavScoringDebugCount = 0;
4142
1
    NavTabbingDir = 0;
4143
1
    NavTabbingCounter = 0;
4144
4145
1
    NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0;
4146
1
    NavJustMovedToKeyMods = ImGuiMod_None;
4147
1
    NavJustMovedToIsTabbing = false;
4148
1
    NavJustMovedToHasSelectionData = false;
4149
4150
    // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac...
4151
    // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this..
4152
1
    ConfigNavWindowingWithGamepad = true;
4153
1
    ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab);
4154
1
    ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab);
4155
1
    NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;
4156
1
    NavWindowingInputSource = ImGuiInputSource_None;
4157
1
    NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;
4158
1
    NavWindowingToggleLayer = false;
4159
1
    NavWindowingToggleKey = ImGuiKey_None;
4160
4161
1
    DimBgRatio = 0.0f;
4162
4163
1
    DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;
4164
1
    DragDropSourceFlags = ImGuiDragDropFlags_None;
4165
1
    DragDropSourceFrameCount = -1;
4166
1
    DragDropMouseButton = -1;
4167
1
    DragDropTargetId = 0;
4168
1
    DragDropTargetFullViewport = 0;
4169
1
    DragDropAcceptFlags = ImGuiDragDropFlags_None;
4170
1
    DragDropAcceptIdCurrRectSurface = 0.0f;
4171
1
    DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;
4172
1
    DragDropAcceptFrameCount = -1;
4173
1
    DragDropHoldJustPressedId = 0;
4174
1
    memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
4175
4176
1
    ClipperTempDataStacked = 0;
4177
4178
1
    CurrentTable = NULL;
4179
1
    TablesTempDataStacked = 0;
4180
1
    CurrentTabBar = NULL;
4181
1
    CurrentMultiSelect = NULL;
4182
1
    MultiSelectTempDataStacked = 0;
4183
4184
1
    HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
4185
1
    HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
4186
4187
1
    MouseCursor = ImGuiMouseCursor_Arrow;
4188
1
    MouseStationaryTimer = 0.0f;
4189
4190
1
    InputTextPasswordFontBackupFlags = ImFontFlags_None;
4191
1
    TempInputId = 0;
4192
1
    memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue));
4193
1
    BeginMenuDepth = BeginComboDepth = 0;
4194
1
    ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;
4195
1
    ColorEditCurrentID = ColorEditSavedID = 0;
4196
1
    ColorEditSavedHue = ColorEditSavedSat = 0.0f;
4197
1
    ColorEditSavedColor = 0;
4198
1
    WindowResizeRelativeMode = false;
4199
1
    ScrollbarSeekMode = 0;
4200
1
    ScrollbarClickDeltaToGrabCenter = 0.0f;
4201
1
    SliderGrabClickOffset = 0.0f;
4202
1
    SliderCurrentAccum = 0.0f;
4203
1
    SliderCurrentAccumDirty = false;
4204
1
    DragCurrentAccumDirty = false;
4205
1
    DragCurrentAccum = 0.0f;
4206
1
    DragSpeedDefaultRatio = 1.0f / 100.0f;
4207
1
    DisabledAlphaBackup = 0.0f;
4208
1
    DisabledStackSize = 0;
4209
1
    TooltipOverrideCount = 0;
4210
1
    TooltipPreviousWindow = NULL;
4211
4212
1
    PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);
4213
1
    PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission
4214
4215
1
    SettingsLoaded = false;
4216
1
    SettingsDirtyTimer = 0.0f;
4217
1
    HookIdNext = 0;
4218
4219
1
    memset(LocalizationTable, 0, sizeof(LocalizationTable));
4220
4221
1
    LogEnabled = false;
4222
1
    LogFlags = ImGuiLogFlags_None;
4223
1
    LogWindow = NULL;
4224
1
    LogNextPrefix = LogNextSuffix = NULL;
4225
1
    LogFile = NULL;
4226
1
    LogLinePosY = FLT_MAX;
4227
1
    LogLineFirstItem = false;
4228
1
    LogDepthRef = 0;
4229
1
    LogDepthToExpand = LogDepthToExpandDefault = 2;
4230
4231
1
    ErrorCallback = NULL;
4232
1
    ErrorCallbackUserData = NULL;
4233
1
    ErrorFirst = true;
4234
1
    ErrorCountCurrentFrame = 0;
4235
1
    StackSizesInBeginForCurrentWindow = NULL;
4236
4237
1
    DebugDrawIdConflictsCount = 0;
4238
1
    DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY;
4239
1
    DebugLocateId = 0;
4240
1
    DebugLogSkippedErrors = 0;
4241
1
    DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
4242
1
    DebugLogAutoDisableFrames = 0;
4243
1
    DebugLocateFrames = 0;
4244
1
    DebugBeginReturnValueCullDepth = -1;
4245
1
    DebugItemPickerActive = false;
4246
1
    DebugItemPickerMouseButton = ImGuiMouseButton_Left;
4247
1
    DebugItemPickerBreakId = 0;
4248
1
    DebugFlashStyleColorTime = 0.0f;
4249
1
    DebugFlashStyleColorIdx = ImGuiCol_COUNT;
4250
4251
    // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
4252
1
    DebugBreakInWindow = 0;
4253
1
    DebugBreakInTable = 0;
4254
1
    DebugBreakInLocateId = false;
4255
1
    DebugBreakKeyChord = ImGuiKey_Pause;
4256
1
    DebugBreakInShortcutRouting = ImGuiKey_None;
4257
4258
1
    memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
4259
1
    FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
4260
1
    FramerateSecPerFrameAccum = 0.0f;
4261
1
    WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;
4262
1
    memset(TempKeychordName, 0, sizeof(TempKeychordName));
4263
1
}
4264
4265
void ImGui::Initialize()
4266
1
{
4267
1
    ImGuiContext& g = *GImGui;
4268
1
    IM_ASSERT(!g.Initialized && !g.SettingsLoaded);
4269
4270
    // Add .ini handle for ImGuiWindow and ImGuiTable types
4271
1
    {
4272
1
        ImGuiSettingsHandler ini_handler;
4273
1
        ini_handler.TypeName = "Window";
4274
1
        ini_handler.TypeHash = ImHashStr("Window");
4275
1
        ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;
4276
1
        ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;
4277
1
        ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;
4278
1
        ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;
4279
1
        ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;
4280
1
        AddSettingsHandler(&ini_handler);
4281
1
    }
4282
1
    TableSettingsAddSettingsHandler();
4283
4284
    // Setup default localization table
4285
1
    LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));
4286
4287
    // Setup default ImGuiPlatformIO clipboard/IME handlers.
4288
1
    g.PlatformIO.Platform_GetClipboardTextFn = Platform_GetClipboardTextFn_DefaultImpl;    // Platform dependent default implementations
4289
1
    g.PlatformIO.Platform_SetClipboardTextFn = Platform_SetClipboardTextFn_DefaultImpl;
4290
1
    g.PlatformIO.Platform_OpenInShellFn = Platform_OpenInShellFn_DefaultImpl;
4291
1
    g.PlatformIO.Platform_SetImeDataFn = Platform_SetImeDataFn_DefaultImpl;
4292
4293
    // Create default viewport
4294
1
    ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();
4295
1
    viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID;
4296
1
    g.Viewports.push_back(viewport);
4297
1
    g.TempBuffer.resize(1024 * 3 + 1, 0);
4298
4299
    // Build KeysMayBeCharInput[] lookup table (1 bool per named key)
4300
156
    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
4301
155
        if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9)
4302
155
            || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period
4303
155
            || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent
4304
155
            || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual)
4305
64
            g.KeysMayBeCharInput.SetBit(key);
4306
4307
#ifdef IMGUI_HAS_DOCK
4308
#endif
4309
4310
    // Print a debug message when running with debug feature IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS because it is very slow.
4311
    // DO NOT COMMENT OUT THIS MESSAGE. IT IS DESIGNED TO REMIND YOU THAT IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS SHOULD ONLY BE TEMPORARILY ENABLED.
4312
#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
4313
    DebugLog("IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n");
4314
#endif
4315
4316
    // ImDrawList/ImFontAtlas are designed to function without ImGui, and 99% of it works without an ImGui context.
4317
    // But this link allows us to facilitate/handle a few edge cases better.
4318
1
    ImFontAtlas* atlas = g.IO.Fonts;
4319
1
    g.DrawListSharedData.Context = &g;
4320
1
    RegisterFontAtlas(atlas);
4321
4322
1
    g.Initialized = true;
4323
1
}
4324
4325
// This function is merely here to free heap allocations.
4326
void ImGui::Shutdown()
4327
1
{
4328
1
    ImGuiContext& g = *GImGui;
4329
1
    IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?");
4330
1
    IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?");
4331
4332
    // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)
4333
1
    for (ImFontAtlas* atlas : g.FontAtlases)
4334
1
    {
4335
1
        UnregisterFontAtlas(atlas);
4336
1
        if (atlas->RefCount == 0)
4337
1
        {
4338
1
            atlas->Locked = false;
4339
1
            IM_DELETE(atlas);
4340
1
        }
4341
1
    }
4342
1
    g.DrawListSharedData.TempBuffer.clear();
4343
4344
    // Cleanup of other data are conditional on actually having initialized Dear ImGui.
4345
1
    if (!g.Initialized)
4346
0
        return;
4347
4348
    // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)
4349
1
    if (g.SettingsLoaded && g.IO.IniFilename != NULL)
4350
1
        SaveIniSettingsToDisk(g.IO.IniFilename);
4351
4352
1
    CallContextHooks(&g, ImGuiContextHookType_Shutdown);
4353
4354
    // Clear everything else
4355
1
    g.Windows.clear_delete();
4356
1
    g.WindowsFocusOrder.clear();
4357
1
    g.WindowsTempSortBuffer.clear();
4358
1
    g.CurrentWindow = NULL;
4359
1
    g.CurrentWindowStack.clear();
4360
1
    g.WindowsById.Clear();
4361
1
    g.NavWindow = NULL;
4362
1
    g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
4363
1
    g.ActiveIdWindow = NULL;
4364
1
    g.MovingWindow = NULL;
4365
4366
1
    g.KeysRoutingTable.Clear();
4367
4368
1
    g.ColorStack.clear();
4369
1
    g.StyleVarStack.clear();
4370
1
    g.FontStack.clear();
4371
1
    g.OpenPopupStack.clear();
4372
1
    g.BeginPopupStack.clear();
4373
1
    g.TreeNodeStack.clear();
4374
4375
1
    g.Viewports.clear_delete();
4376
4377
1
    g.TabBars.Clear();
4378
1
    g.CurrentTabBarStack.clear();
4379
1
    g.ShrinkWidthBuffer.clear();
4380
4381
1
    g.ClipperTempData.clear_destruct();
4382
4383
1
    g.Tables.Clear();
4384
1
    g.TablesTempData.clear_destruct();
4385
1
    g.DrawChannelsTempMergeBuffer.clear();
4386
4387
1
    g.MultiSelectStorage.Clear();
4388
1
    g.MultiSelectTempData.clear_destruct();
4389
4390
1
    g.ClipboardHandlerData.clear();
4391
1
    g.MenusIdSubmittedThisFrame.clear();
4392
1
    g.InputTextState.ClearFreeMemory();
4393
1
    g.InputTextLineIndex.clear();
4394
1
    g.InputTextDeactivatedState.ClearFreeMemory();
4395
4396
1
    g.SettingsWindows.clear();
4397
1
    g.SettingsHandlers.clear();
4398
4399
1
    if (g.LogFile)
4400
0
    {
4401
0
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
4402
0
        if (g.LogFile != stdout)
4403
0
#endif
4404
0
            ImFileClose(g.LogFile);
4405
0
        g.LogFile = NULL;
4406
0
    }
4407
1
    g.LogBuffer.clear();
4408
1
    g.DebugLogBuf.clear();
4409
1
    g.DebugLogIndex.clear();
4410
4411
1
    g.Initialized = false;
4412
1
}
4413
4414
// No specific ordering/dependency support, will see as needed
4415
ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)
4416
0
{
4417
0
    ImGuiContext& g = *ctx;
4418
0
    IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);
4419
0
    g.Hooks.push_back(*hook);
4420
0
    g.Hooks.back().HookId = ++g.HookIdNext;
4421
0
    return g.HookIdNext;
4422
0
}
4423
4424
// Deferred removal, avoiding issue with changing vector while iterating it
4425
void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)
4426
0
{
4427
0
    ImGuiContext& g = *ctx;
4428
0
    IM_ASSERT(hook_id != 0);
4429
0
    for (ImGuiContextHook& hook : g.Hooks)
4430
0
        if (hook.HookId == hook_id)
4431
0
            hook.Type = ImGuiContextHookType_PendingRemoval_;
4432
0
}
4433
4434
// Call context hooks (used by e.g. test engine)
4435
// We assume a small number of hooks so all stored in same array
4436
void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)
4437
483k
{
4438
483k
    ImGuiContext& g = *ctx;
4439
483k
    for (ImGuiContextHook& hook : g.Hooks)
4440
0
        if (hook.Type == hook_type)
4441
0
            hook.Callback(&g, &hook);
4442
483k
}
4443
4444
//-----------------------------------------------------------------------------
4445
// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)
4446
//-----------------------------------------------------------------------------
4447
4448
// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods
4449
3
ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)
4450
3
{
4451
3
    memset(this, 0, sizeof(*this));
4452
3
    Ctx = ctx;
4453
3
    Name = ImStrdup(name);
4454
3
    NameBufLen = (int)ImStrlen(name) + 1;
4455
3
    ID = ImHashStr(name);
4456
3
    IDStack.push_back(ID);
4457
3
    MoveId = GetID("#MOVE");
4458
3
    ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
4459
3
    ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
4460
3
    AutoFitFramesX = AutoFitFramesY = -1;
4461
3
    AutoPosLastDirection = ImGuiDir_None;
4462
3
    SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0;
4463
3
    SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
4464
3
    LastFrameActive = -1;
4465
3
    LastTimeActive = -1.0f;
4466
3
    FontRefSize = 0.0f;
4467
3
    FontWindowScale = FontWindowScaleParents = 1.0f;
4468
3
    SettingsOffset = -1;
4469
3
    DrawList = &DrawListInst;
4470
3
    DrawList->_OwnerName = Name;
4471
3
    DrawList->_SetDrawListSharedData(&Ctx->DrawListSharedData);
4472
3
    NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);
4473
3
}
4474
4475
ImGuiWindow::~ImGuiWindow()
4476
3
{
4477
3
    IM_ASSERT(DrawList == &DrawListInst);
4478
3
    IM_DELETE(Name);
4479
3
    ColumnsStorage.clear_destruct();
4480
3
}
4481
4482
static void SetCurrentWindow(ImGuiWindow* window)
4483
323k
{
4484
323k
    ImGuiContext& g = *GImGui;
4485
323k
    g.CurrentWindow = window;
4486
323k
    g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL;
4487
323k
    g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;
4488
323k
    g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking
4489
323k
    if (window)
4490
243k
    {
4491
243k
        bool backup_skip_items = window->SkipItems;
4492
243k
        window->SkipItems = false;
4493
243k
        if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
4494
243k
        {
4495
243k
            ImGuiViewport* viewport = window->Viewport;
4496
243k
            g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity()
4497
243k
        }
4498
243k
        ImGui::UpdateCurrentFontSize(0.0f);
4499
243k
        window->SkipItems = backup_skip_items;
4500
243k
        ImGui::NavUpdateCurrentWindowIsScrollPushableX();
4501
243k
    }
4502
323k
}
4503
4504
void ImGui::GcCompactTransientMiscBuffers()
4505
0
{
4506
0
    ImGuiContext& g = *GImGui;
4507
0
    g.ItemFlagsStack.clear();
4508
0
    g.GroupStack.clear();
4509
0
    g.InputTextLineIndex.clear();
4510
0
    g.MultiSelectTempDataStacked = 0;
4511
0
    g.MultiSelectTempData.clear_destruct();
4512
0
    TableGcCompactSettings();
4513
0
    for (ImFontAtlas* atlas : g.FontAtlases)
4514
0
        atlas->CompactCache();
4515
0
}
4516
4517
// Free up/compact internal window buffers, we can use this when a window becomes unused.
4518
// Not freed:
4519
// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)
4520
// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.
4521
void ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)
4522
1
{
4523
1
    window->MemoryCompacted = true;
4524
1
    window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;
4525
1
    window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;
4526
1
    window->IDStack.clear();
4527
1
    window->DrawList->_ClearFreeMemory();
4528
1
    window->DC.ChildWindows.clear();
4529
1
    window->DC.ItemWidthStack.clear();
4530
1
    window->DC.TextWrapPosStack.clear();
4531
1
}
4532
4533
void ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)
4534
0
{
4535
    // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.
4536
    // The other buffers tends to amortize much faster.
4537
0
    window->MemoryCompacted = false;
4538
0
    window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);
4539
0
    window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);
4540
0
    window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;
4541
0
}
4542
4543
void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)
4544
2
{
4545
2
    ImGuiContext& g = *GImGui;
4546
4547
    // Clear previous active id
4548
2
    if (g.ActiveId != 0)
4549
0
    {
4550
        // Store deactivate data
4551
0
        ImGuiDeactivatedItemData* deactivated_data = &g.DeactivatedItemData;
4552
0
        deactivated_data->ID = g.ActiveId;
4553
0
        deactivated_data->ElapseFrame = (g.LastItemData.ID == g.ActiveId) ? g.FrameCount : g.FrameCount + 1; // FIXME: OK to use LastItemData?
4554
0
        deactivated_data->HasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;
4555
0
        deactivated_data->IsAlive = (g.ActiveIdIsAlive == g.ActiveId);
4556
4557
        // This could be written in a more general way (e.g associate a hook to ActiveId),
4558
        // but since this is currently quite an exception we'll leave it as is.
4559
        // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveID()
4560
0
        if (g.InputTextState.ID == g.ActiveId)
4561
0
            InputTextDeactivateHook(g.ActiveId);
4562
4563
        // While most behaved code would make an effort to not steal active id during window move/drag operations,
4564
        // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch
4565
        // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.
4566
0
        if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)
4567
0
        {
4568
0
            IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n");
4569
0
            StopMouseMovingWindow();
4570
0
        }
4571
0
    }
4572
4573
    // Set active id
4574
2
    g.ActiveIdIsJustActivated = (g.ActiveId != id);
4575
2
    if (g.ActiveIdIsJustActivated)
4576
0
    {
4577
0
        IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() old:0x%08X (window \"%s\") -> new:0x%08X (window \"%s\")\n", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : "", id, window ? window->Name : "");
4578
0
        g.ActiveIdTimer = 0.0f;
4579
0
        g.ActiveIdHasBeenPressedBefore = false;
4580
0
        g.ActiveIdHasBeenEditedBefore = false;
4581
0
        g.ActiveIdMouseButton = -1;
4582
0
        if (id != 0)
4583
0
        {
4584
0
            g.LastActiveId = id;
4585
0
            g.LastActiveIdTimer = 0.0f;
4586
0
        }
4587
0
    }
4588
2
    g.ActiveId = id;
4589
2
    g.ActiveIdAllowOverlap = false;
4590
2
    g.ActiveIdNoClearOnFocusLoss = false;
4591
2
    g.ActiveIdWindow = window;
4592
2
    g.ActiveIdHasBeenEditedThisFrame = false;
4593
2
    g.ActiveIdFromShortcut = false;
4594
2
    g.ActiveIdDisabledId = 0;
4595
2
    if (id)
4596
0
    {
4597
0
        g.ActiveIdIsAlive = id;
4598
0
        g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
4599
0
        IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);
4600
0
    }
4601
4602
    // Clear declaration of inputs claimed by the widget
4603
    // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)
4604
2
    g.ActiveIdUsingNavDirMask = 0x00;
4605
2
    g.ActiveIdUsingAllKeyboardKeys = false;
4606
2
}
4607
4608
void ImGui::ClearActiveID()
4609
2
{
4610
2
    SetActiveID(0, NULL); // g.ActiveId = 0;
4611
2
}
4612
4613
void ImGui::SetHoveredID(ImGuiID id)
4614
1.15k
{
4615
1.15k
    ImGuiContext& g = *GImGui;
4616
1.15k
    g.HoveredId = id;
4617
1.15k
    g.HoveredIdAllowOverlap = false;
4618
1.15k
    if (id != 0 && g.HoveredIdPreviousFrame != id)
4619
4
        g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;
4620
1.15k
}
4621
4622
ImGuiID ImGui::GetHoveredID()
4623
0
{
4624
0
    ImGuiContext& g = *GImGui;
4625
0
    return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;
4626
0
}
4627
4628
void ImGui::MarkItemEdited(ImGuiID id)
4629
2
{
4630
    // This marking is to be able to provide info for IsItemDeactivatedAfterEdit().
4631
    // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.
4632
2
    ImGuiContext& g = *GImGui;
4633
2
    if (g.LastItemData.ItemFlags & ImGuiItemFlags_NoMarkEdited)
4634
0
        return;
4635
2
    if (g.ActiveId == id || g.ActiveId == 0)
4636
2
    {
4637
        // FIXME: Can't we fully rely on LastItemData yet?
4638
2
        g.ActiveIdHasBeenEditedThisFrame = true;
4639
2
        g.ActiveIdHasBeenEditedBefore = true;
4640
2
        if (g.DeactivatedItemData.ID == id)
4641
0
            g.DeactivatedItemData.HasBeenEditedBefore = true;
4642
2
    }
4643
4644
    // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)
4645
    // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)
4646
    // FIXME: This assert is getting a bit meaningless over time. It helped detect some unusual use cases but eventually it is becoming an unnecessary restriction.
4647
2
    IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id || g.NavJustMovedToId || (g.CurrentMultiSelect != NULL && g.BoxSelectState.IsActive));
4648
4649
    //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);
4650
2
    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
4651
2
}
4652
4653
bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
4654
924
{
4655
    // An active popup disable hovering on other windows (apart from its own children)
4656
    // FIXME-OPT: This could be cached/stored within the window.
4657
924
    ImGuiContext& g = *GImGui;
4658
924
    if (g.NavWindow)
4659
405
        if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
4660
405
            if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
4661
0
            {
4662
                // For the purpose of those flags we differentiate "standard popup" from "modal popup"
4663
                // NB: The 'else' is important because Modal windows are also Popups.
4664
0
                bool want_inhibit = false;
4665
0
                if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
4666
0
                    want_inhibit = true;
4667
0
                else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
4668
0
                    want_inhibit = true;
4669
4670
                // Inhibit hover unless the window is within the stack of our modal/popup
4671
0
                if (want_inhibit)
4672
0
                    if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))
4673
0
                        return false;
4674
0
            }
4675
924
    return true;
4676
924
}
4677
4678
static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
4679
0
{
4680
0
    ImGuiContext& g = *GImGui;
4681
0
    if (flags & ImGuiHoveredFlags_DelayNormal)
4682
0
        return g.Style.HoverDelayNormal;
4683
0
    if (flags & ImGuiHoveredFlags_DelayShort)
4684
0
        return g.Style.HoverDelayShort;
4685
0
    return 0.0f;
4686
0
}
4687
4688
static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)
4689
0
{
4690
    // Allow instance flags to override shared flags
4691
0
    if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))
4692
0
        shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);
4693
0
    return user_flags | shared_flags;
4694
0
}
4695
4696
// This is roughly matching the behavior of internal-facing ItemHoverable()
4697
// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()
4698
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
4699
bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
4700
0
{
4701
0
    ImGuiContext& g = *GImGui;
4702
0
    ImGuiWindow* window = g.CurrentWindow;
4703
0
    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0, "Invalid flags for IsItemHovered()!");
4704
4705
0
    if (g.NavHighlightItemUnderNav && g.NavCursorVisible && !(flags & ImGuiHoveredFlags_NoNavOverride))
4706
0
    {
4707
0
        if (!IsItemFocused())
4708
0
            return false;
4709
0
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4710
0
            return false;
4711
4712
0
        if (flags & ImGuiHoveredFlags_ForTooltip)
4713
0
            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);
4714
0
    }
4715
0
    else
4716
0
    {
4717
        // Test for bounding box overlap, as updated as ItemAdd()
4718
0
        ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
4719
0
        if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
4720
0
            return false;
4721
4722
0
        if (flags & ImGuiHoveredFlags_ForTooltip)
4723
0
            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
4724
4725
        // Done with rectangle culling so we can perform heavier checks now
4726
        // Test if we are hovering the right window (our window could be behind another window)
4727
        // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)
4728
        // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable
4729
        // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was
4730
        // the test that has been running for a long while.
4731
0
        if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)
4732
0
            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)
4733
0
                return false;
4734
4735
        // Test if another item is active (e.g. being dragged)
4736
0
        const ImGuiID id = g.LastItemData.ID;
4737
0
        if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)
4738
0
            if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4739
0
            {
4740
                // When ActiveId == MoveId it means that either:
4741
                // - (1) user clicked on void _or_ an item with no id, which triggers moving window (ActiveId is set even when window has _NoMove flag)
4742
                //   - the (id == 0) test handles it, however, IsItemHovered() will leak between id==0 items (mostly visible when using _NoMove). // FIXME: May be fixed.
4743
                // - (2) user clicked a disabled item. UpdateMouseMovingWindowEndFrame() uses ActiveId == MoveId to avoid interference with item logic + sets ActiveIdDisabledId.
4744
0
                bool cancel_is_hovered = true;
4745
0
                if (g.ActiveId == window->MoveId && (id == 0 || g.ActiveIdDisabledId == id))
4746
0
                    cancel_is_hovered = false;
4747
0
                if (cancel_is_hovered)
4748
0
                    return false;
4749
0
            }
4750
4751
        // Test if interactions on this window are blocked by an active popup or modal.
4752
        // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.
4753
0
        if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_NoWindowHoverableCheck))
4754
0
            return false;
4755
4756
        // Test if the item is disabled
4757
0
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
4758
0
            return false;
4759
4760
        // Special handling for calling after Begin() which represent the title bar or tab.
4761
        // When the window is skipped/collapsed (SkipItems==true) that last item (always ->MoveId submitted by Begin)
4762
        // will never be overwritten so we need to detect the case.
4763
0
        if (id == window->MoveId && window->WriteAccessed)
4764
0
            return false;
4765
4766
        // Test if using AllowOverlap and overlapped
4767
0
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap) && id != 0)
4768
0
            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)
4769
0
                if (g.HoveredIdPreviousFrame != g.LastItemData.ID)
4770
0
                    return false;
4771
0
    }
4772
4773
    // Handle hover delay
4774
    // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
4775
0
    const float delay = CalcDelayFromHoveredFlags(flags);
4776
0
    if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
4777
0
    {
4778
0
        ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromPos(g.LastItemData.Rect.Min);
4779
0
        if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
4780
0
            g.HoverItemDelayTimer = 0.0f;
4781
0
        g.HoverItemDelayId = hover_delay_id;
4782
4783
        // When changing hovered item we requires a bit of stationary delay before activating hover timer,
4784
        // but once unlocked on a given item we also moving.
4785
        //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
4786
0
        if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
4787
0
            return false;
4788
4789
0
        if (g.HoverItemDelayTimer < delay)
4790
0
            return false;
4791
0
    }
4792
4793
0
    return true;
4794
0
}
4795
4796
// Internal facing ItemHoverable() used when submitting widgets. THIS IS A SUBMISSION NOT A HOVER CHECK.
4797
// Returns whether the item was hovered, logic differs slightly from IsItemHovered().
4798
// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)
4799
// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.
4800
// If you used this in your legacy/custom widgets code:
4801
// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.ItemFlags'.
4802
// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.
4803
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)
4804
324k
{
4805
324k
    ImGuiContext& g = *GImGui;
4806
324k
    ImGuiWindow* window = g.CurrentWindow;
4807
4808
    // Detect ID conflicts
4809
    // (this is specifically done here by comparing on hover because it allows us a detection of duplicates that is algorithmically extra cheap, 1 u32 compare per item. No O(log N) lookup whatsoever)
4810
324k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4811
324k
    if (id != 0 && g.HoveredIdPreviousFrame == id && (item_flags & ImGuiItemFlags_AllowDuplicateId) == 0)
4812
1.15k
    {
4813
1.15k
        g.HoveredIdPreviousFrameItemCount++;
4814
1.15k
        if (g.DebugDrawIdConflictsId == id)
4815
0
            window->DrawList->AddRect(bb.Min - ImVec2(1,1), bb.Max + ImVec2(1,1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
4816
1.15k
    }
4817
324k
#endif
4818
4819
324k
    if (g.HoveredWindow != window)
4820
320k
        return false;
4821
3.57k
    if (!IsMouseHoveringRect(bb.Min, bb.Max))
4822
2.41k
        return false;
4823
4824
1.15k
    if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)
4825
0
        return false;
4826
1.15k
    if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)
4827
0
        if (!g.ActiveIdFromShortcut)
4828
0
            return false;
4829
4830
    // We are done with rectangle culling so we can perform heavier checks now.
4831
1.15k
    if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))
4832
0
    {
4833
0
        g.HoveredIdIsDisabled = true;
4834
0
        return false;
4835
0
    }
4836
4837
    // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level
4838
    // hover test in widgets code. We could also decide to split this function is two.
4839
1.15k
    if (id != 0)
4840
1.15k
    {
4841
        // Drag source doesn't report as hovered
4842
1.15k
        if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))
4843
0
            return false;
4844
4845
1.15k
        SetHoveredID(id);
4846
4847
        // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.
4848
        // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.
4849
1.15k
        if (item_flags & ImGuiItemFlags_AllowOverlap)
4850
0
        {
4851
0
            g.HoveredIdAllowOverlap = true;
4852
0
            if (g.HoveredIdPreviousFrame != id)
4853
0
                return false;
4854
0
        }
4855
4856
        // Display shortcut (only works with mouse)
4857
        // (ImGuiItemStatusFlags_HasShortcut in LastItemData denotes we want a tooltip)
4858
1.15k
        if (id == g.LastItemData.ID && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasShortcut) && g.ActiveId != id)
4859
0
            if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal))
4860
0
                SetTooltip("%s", GetKeyChordName(g.LastItemData.Shortcut));
4861
1.15k
    }
4862
4863
    // When disabled we'll return false but still set HoveredId
4864
1.15k
    if (item_flags & ImGuiItemFlags_Disabled)
4865
0
    {
4866
        // Release active id if turning disabled
4867
0
        if (g.ActiveId == id && id != 0)
4868
0
            ClearActiveID();
4869
0
        g.HoveredIdIsDisabled = true;
4870
0
        return false;
4871
0
    }
4872
4873
1.15k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4874
1.15k
    if (id != 0)
4875
1.15k
    {
4876
        // [DEBUG] Item Picker tool!
4877
        // We perform the check here because reaching is path is rare (1~ time a frame),
4878
        // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered
4879
        // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost.
4880
1.15k
        if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)
4881
0
            GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
4882
1.15k
        if (g.DebugItemPickerBreakId == id)
4883
0
            IM_DEBUG_BREAK();
4884
1.15k
    }
4885
1.15k
#endif
4886
4887
1.15k
    if (g.NavHighlightItemUnderNav && (item_flags & ImGuiItemFlags_NoNavDisableMouseHover) == 0)
4888
0
        return false;
4889
4890
1.15k
    return true;
4891
1.15k
}
4892
4893
// FIXME: This is inlined/duplicated in ItemAdd()
4894
// FIXME: The id != 0 path is not used by our codebase, may get rid of it?
4895
bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)
4896
0
{
4897
0
    ImGuiContext& g = *GImGui;
4898
0
    ImGuiWindow* window = g.CurrentWindow;
4899
0
    if (!bb.Overlaps(window->ClipRect))
4900
0
        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
4901
0
            if (!g.ItemUnclipByLog)
4902
0
                return true;
4903
0
    return false;
4904
0
}
4905
4906
// This is also inlined in ItemAdd()
4907
// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.
4908
void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect)
4909
161k
{
4910
161k
    ImGuiContext& g = *GImGui;
4911
161k
    g.LastItemData.ID = item_id;
4912
161k
    g.LastItemData.ItemFlags = item_flags;
4913
161k
    g.LastItemData.StatusFlags = status_flags;
4914
161k
    g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;
4915
161k
}
4916
4917
static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect)
4918
161k
{
4919
161k
    ImGuiContext& g = *GImGui;
4920
161k
    SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect);
4921
161k
}
4922
4923
static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect)
4924
0
{
4925
0
    ImGuiContext& g = *GImGui;
4926
0
    SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect);
4927
0
}
4928
4929
float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)
4930
0
{
4931
0
    if (wrap_pos_x < 0.0f)
4932
0
        return 0.0f;
4933
4934
0
    ImGuiContext& g = *GImGui;
4935
0
    ImGuiWindow* window = g.CurrentWindow;
4936
0
    if (wrap_pos_x == 0.0f)
4937
0
    {
4938
        // We could decide to setup a default wrapping max point for auto-resizing windows,
4939
        // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?
4940
        //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))
4941
        //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);
4942
        //else
4943
0
        wrap_pos_x = window->WorkRect.Max.x;
4944
0
    }
4945
0
    else if (wrap_pos_x > 0.0f)
4946
0
    {
4947
0
        wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space
4948
0
    }
4949
4950
0
    return ImMax(wrap_pos_x - pos.x, 1.0f);
4951
0
}
4952
4953
// IM_ALLOC() == ImGui::MemAlloc()
4954
void* ImGui::MemAlloc(size_t size)
4955
243
{
4956
243
    void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);
4957
243
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4958
243
    if (ImGuiContext* ctx = GImGui)
4959
240
        DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);
4960
243
#endif
4961
243
    return ptr;
4962
243
}
4963
4964
// IM_FREE() == ImGui::MemFree()
4965
void ImGui::MemFree(void* ptr)
4966
243
{
4967
243
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4968
243
    if (ptr != NULL)
4969
243
        if (ImGuiContext* ctx = GImGui)
4970
229
            DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);
4971
243
#endif
4972
243
    return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);
4973
243
}
4974
4975
// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames"
4976
void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)
4977
469
{
4978
469
    ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4979
469
    IM_UNUSED(ptr);
4980
469
    if (entry->FrameCount != frame_count)
4981
7
    {
4982
7
        info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);
4983
7
        entry = &info->LastEntriesBuf[info->LastEntriesIdx];
4984
7
        entry->FrameCount = frame_count;
4985
7
        entry->AllocCount = entry->FreeCount = 0;
4986
7
    }
4987
469
    if (size != (size_t)-1)
4988
240
    {
4989
        //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, (int)size, ptr);
4990
240
        entry->AllocCount++;
4991
240
        info->TotalAllocCount++;
4992
240
    }
4993
229
    else
4994
229
    {
4995
        //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr);
4996
229
        entry->FreeCount++;
4997
229
        info->TotalFreeCount++;
4998
229
    }
4999
469
}
5000
5001
const char* ImGui::GetClipboardText()
5002
0
{
5003
0
    ImGuiContext& g = *GImGui;
5004
0
    return g.PlatformIO.Platform_GetClipboardTextFn ? g.PlatformIO.Platform_GetClipboardTextFn(&g) : "";
5005
0
}
5006
5007
void ImGui::SetClipboardText(const char* text)
5008
0
{
5009
0
    ImGuiContext& g = *GImGui;
5010
0
    if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
5011
0
        g.PlatformIO.Platform_SetClipboardTextFn(&g, text);
5012
0
}
5013
5014
const char* ImGui::GetVersion()
5015
0
{
5016
0
    return IMGUI_VERSION;
5017
0
}
5018
5019
ImGuiIO& ImGui::GetIO()
5020
973k
{
5021
973k
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5022
973k
    return GImGui->IO;
5023
973k
}
5024
5025
// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
5026
ImGuiIO& ImGui::GetIO(ImGuiContext* ctx)
5027
0
{
5028
0
    IM_ASSERT(ctx != NULL);
5029
0
    return ctx->IO;
5030
0
}
5031
5032
ImGuiPlatformIO& ImGui::GetPlatformIO()
5033
161k
{
5034
161k
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext()?");
5035
161k
    return GImGui->PlatformIO;
5036
161k
}
5037
5038
// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856)
5039
ImGuiPlatformIO& ImGui::GetPlatformIO(ImGuiContext* ctx)
5040
0
{
5041
0
    IM_ASSERT(ctx != NULL);
5042
0
    return ctx->PlatformIO;
5043
0
}
5044
5045
// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()
5046
ImDrawData* ImGui::GetDrawData()
5047
80.5k
{
5048
80.5k
    ImGuiContext& g = *GImGui;
5049
80.5k
    ImGuiViewportP* viewport = g.Viewports[0];
5050
80.5k
    return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;
5051
80.5k
}
5052
5053
double ImGui::GetTime()
5054
0
{
5055
0
    return GImGui->Time;
5056
0
}
5057
5058
int ImGui::GetFrameCount()
5059
4
{
5060
4
    return GImGui->FrameCount;
5061
4
}
5062
5063
static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)
5064
0
{
5065
    // Create the draw list on demand, because they are not frequently used for all viewports
5066
0
    ImGuiContext& g = *GImGui;
5067
0
    IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));
5068
0
    ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];
5069
0
    if (draw_list == NULL)
5070
0
    {
5071
0
        draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);
5072
0
        draw_list->_OwnerName = drawlist_name;
5073
0
        viewport->BgFgDrawLists[drawlist_no] = draw_list;
5074
0
    }
5075
5076
    // Our ImDrawList system requires that there is always a command
5077
0
    if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)
5078
0
    {
5079
0
        draw_list->_ResetForNewFrame();
5080
0
        draw_list->PushTexture(g.IO.Fonts->TexRef);
5081
0
        draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);
5082
0
        viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;
5083
0
    }
5084
0
    return draw_list;
5085
0
}
5086
5087
ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)
5088
0
{
5089
0
    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background");
5090
0
}
5091
5092
ImDrawList* ImGui::GetBackgroundDrawList()
5093
0
{
5094
0
    ImGuiContext& g = *GImGui;
5095
0
    return GetBackgroundDrawList(g.Viewports[0]);
5096
0
}
5097
5098
ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)
5099
0
{
5100
0
    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground");
5101
0
}
5102
5103
ImDrawList* ImGui::GetForegroundDrawList()
5104
0
{
5105
0
    ImGuiContext& g = *GImGui;
5106
0
    return GetForegroundDrawList(g.Viewports[0]);
5107
0
}
5108
5109
ImDrawListSharedData* ImGui::GetDrawListSharedData()
5110
0
{
5111
0
    return &GImGui->DrawListSharedData;
5112
0
}
5113
5114
void ImGui::StartMouseMovingWindow(ImGuiWindow* window)
5115
0
{
5116
    // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.
5117
    // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.
5118
    // This is because we want ActiveId to be set even when the window is not permitted to move.
5119
0
    ImGuiContext& g = *GImGui;
5120
0
    FocusWindow(window);
5121
0
    SetActiveID(window->MoveId, window);
5122
0
    if (g.IO.ConfigNavCursorVisibleAuto)
5123
0
        g.NavCursorVisible = false;
5124
0
    g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;
5125
0
    g.ActiveIdNoClearOnFocusLoss = true;
5126
0
    SetActiveIdUsingAllKeyboardKeys();
5127
5128
0
    bool can_move_window = true;
5129
0
    if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))
5130
0
        can_move_window = false;
5131
0
    if (can_move_window)
5132
0
        g.MovingWindow = window;
5133
0
}
5134
5135
// This is not 100% symetric with StartMouseMovingWindow().
5136
// We do NOT clear ActiveID, because:
5137
// - It would lead to rather confusing recursive code paths. Caller can call ClearActiveID() if desired.
5138
// - Some code intentionally cancel moving but keep the ActiveID to lock inputs (e.g. code path taken when clicking a disabled item).
5139
void ImGui::StopMouseMovingWindow()
5140
0
{
5141
0
    ImGuiContext& g = *GImGui;
5142
5143
    // [nb: docking branch has more stuff in this function]
5144
5145
0
    g.MovingWindow = NULL;
5146
0
}
5147
5148
// Handle mouse moving window
5149
// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()
5150
// FIXME: We don't have strong guarantee that g.MovingWindow stay synced with g.ActiveId == g.MovingWindow->MoveId.
5151
// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,
5152
// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.
5153
void ImGui::UpdateMouseMovingWindowNewFrame()
5154
80.5k
{
5155
80.5k
    ImGuiContext& g = *GImGui;
5156
80.5k
    if (g.MovingWindow != NULL)
5157
0
    {
5158
        // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).
5159
        // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.
5160
0
        KeepAliveID(g.ActiveId);
5161
0
        IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);
5162
0
        ImGuiWindow* moving_window = g.MovingWindow->RootWindow;
5163
0
        if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))
5164
0
        {
5165
0
            ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;
5166
0
            SetWindowPos(moving_window, pos, ImGuiCond_Always);
5167
0
            FocusWindow(g.MovingWindow);
5168
0
        }
5169
0
        else
5170
0
        {
5171
0
            StopMouseMovingWindow();
5172
0
            ClearActiveID();
5173
0
        }
5174
0
    }
5175
80.5k
    else
5176
80.5k
    {
5177
        // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.
5178
80.5k
        if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)
5179
0
        {
5180
0
            KeepAliveID(g.ActiveId);
5181
0
            if (!g.IO.MouseDown[0])
5182
0
                ClearActiveID();
5183
0
        }
5184
80.5k
    }
5185
80.5k
}
5186
5187
// Initiate focusing and moving window when clicking on empty space or title bar.
5188
// Initiate focusing window when clicking on a disabled item.
5189
// Handle left-click and right-click focus.
5190
void ImGui::UpdateMouseMovingWindowEndFrame()
5191
80.5k
{
5192
80.5k
    ImGuiContext& g = *GImGui;
5193
80.5k
    if (g.ActiveId != 0 || (g.HoveredId != 0 && !g.HoveredIdIsDisabled))
5194
1.15k
        return;
5195
5196
    // Unless we just made a window/popup appear
5197
79.4k
    if (g.NavWindow && g.NavWindow->Appearing)
5198
1
        return;
5199
5200
    // Click on empty space to focus window and start moving
5201
    // (after we're done with all our widgets)
5202
79.4k
    if (g.IO.MouseClicked[0])
5203
0
    {
5204
        // Handle the edge case of a popup being closed while clicking in its empty space.
5205
        // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.
5206
0
        ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
5207
0
        const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);
5208
5209
0
        if (root_window != NULL && !is_closed_popup)
5210
0
        {
5211
0
            StartMouseMovingWindow(g.HoveredWindow); //-V595
5212
5213
            // FIXME: In principal we might be able to call StopMouseMovingWindow() below.
5214
            // Please note how StartMouseMovingWindow() and StopMouseMovingWindow() and not entirely symetrical, at the later doesn't clear ActiveId.
5215
5216
            // Cancel moving if clicked outside of title bar
5217
0
            if (g.IO.ConfigWindowsMoveFromTitleBarOnly)
5218
0
                if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar))
5219
0
                    if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))
5220
0
                        g.MovingWindow = NULL;
5221
5222
            // Cancel moving if clicked over an item which was disabled or inhibited by popups
5223
            // (when g.HoveredIdIsDisabled == true && g.HoveredId == 0 we are inhibited by popups, when g.HoveredIdIsDisabled == true && g.HoveredId != 0 we are over a disabled item)
5224
0
            if (g.HoveredIdIsDisabled)
5225
0
            {
5226
0
                g.MovingWindow = NULL;
5227
0
                g.ActiveIdDisabledId = g.HoveredId;
5228
0
            }
5229
0
        }
5230
0
        else if (root_window == NULL && g.NavWindow != NULL)
5231
0
        {
5232
            // Clicking on void disable focus
5233
0
            FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);
5234
0
        }
5235
0
    }
5236
5237
    // With right mouse button we close popups without changing focus based on where the mouse is aimed
5238
    // Instead, focus will be restored to the window under the bottom-most closed popup.
5239
    // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)
5240
79.4k
    if (g.IO.MouseClicked[1] && g.HoveredId == 0)
5241
0
    {
5242
        // Find the top-most window between HoveredWindow and the top-most Modal Window.
5243
        // This is where we can trim the popup stack.
5244
0
        ImGuiWindow* modal = GetTopMostPopupModal();
5245
0
        bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));
5246
0
        ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);
5247
0
    }
5248
79.4k
}
5249
5250
static bool IsWindowActiveAndVisible(ImGuiWindow* window)
5251
240k
{
5252
240k
    return (window->Active) && (!window->Hidden);
5253
240k
}
5254
5255
// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)
5256
void ImGui::UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos)
5257
80.5k
{
5258
80.5k
    ImGuiContext& g = *GImGui;
5259
80.5k
    ImGuiIO& io = g.IO;
5260
5261
    // FIXME-DPI: This storage was added on 2021/03/31 for test engine, but if we want to multiply WINDOWS_HOVER_PADDING
5262
    // by DpiScale, we need to make this window-agnostic anyhow, maybe need storing inside ImGuiWindow.
5263
80.5k
    g.WindowsBorderHoverPadding = ImMax(ImMax(g.Style.TouchExtraPadding.x, g.Style.TouchExtraPadding.y), g.Style.WindowBorderHoverPadding);
5264
5265
    // Find the window hovered by mouse:
5266
    // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.
5267
    // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
5268
    // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
5269
80.5k
    bool clear_hovered_windows = false;
5270
80.5k
    FindHoveredWindowEx(mouse_pos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow);
5271
80.5k
    g.HoveredWindowBeforeClear = g.HoveredWindow;
5272
5273
    // Modal windows prevents mouse from hovering behind them.
5274
80.5k
    ImGuiWindow* modal_window = GetTopMostPopupModal();
5275
80.5k
    if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))
5276
0
        clear_hovered_windows = true;
5277
5278
    // Disabled mouse hovering (we don't currently clear MousePos, we could)
5279
80.5k
    if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
5280
0
        clear_hovered_windows = true;
5281
5282
    // We track click ownership. When clicked outside of a window the click is owned by the application and
5283
    // won't report hovering nor request capture even while dragging over our windows afterward.
5284
80.5k
    const bool has_open_popup = (g.OpenPopupStack.Size > 0);
5285
80.5k
    const bool has_open_modal = (modal_window != NULL);
5286
80.5k
    int mouse_earliest_down = -1;
5287
80.5k
    bool mouse_any_down = false;
5288
483k
    for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
5289
402k
    {
5290
402k
        if (io.MouseClicked[i])
5291
2
        {
5292
2
            io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;
5293
2
            io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;
5294
2
        }
5295
402k
        mouse_any_down |= io.MouseDown[i];
5296
402k
        if (io.MouseDown[i] || io.MouseReleased[i]) // Increase release frame for our evaluation of earliest button (#1392)
5297
18
            if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])
5298
18
                mouse_earliest_down = i;
5299
402k
    }
5300
80.5k
    const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];
5301
80.5k
    const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];
5302
5303
    // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.
5304
    // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
5305
80.5k
    const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
5306
80.5k
    if (!mouse_avail && !mouse_dragging_extern_payload)
5307
0
        clear_hovered_windows = true;
5308
5309
80.5k
    if (clear_hovered_windows)
5310
0
        g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;
5311
5312
    // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)
5313
    // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag
5314
80.5k
    if (g.WantCaptureMouseNextFrame != -1)
5315
0
    {
5316
0
        io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);
5317
0
    }
5318
80.5k
    else
5319
80.5k
    {
5320
80.5k
        io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;
5321
80.5k
        io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;
5322
80.5k
    }
5323
5324
    // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)
5325
80.5k
    io.WantCaptureKeyboard = false;
5326
80.5k
    if ((io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) == 0)
5327
80.5k
    {
5328
80.5k
        if ((g.ActiveId != 0) || (modal_window != NULL))
5329
0
            io.WantCaptureKeyboard = true;
5330
80.5k
        else if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && io.ConfigNavCaptureKeyboard)
5331
662
            io.WantCaptureKeyboard = true;
5332
80.5k
    }
5333
80.5k
    if (g.WantCaptureKeyboardNextFrame != -1) // Manual override
5334
0
        io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);
5335
5336
    // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible
5337
80.5k
    io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;
5338
80.5k
}
5339
5340
// Called once a frame. Followed by SetCurrentFont() which sets up the remaining data.
5341
// FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal!
5342
static void SetupDrawListSharedData()
5343
80.5k
{
5344
80.5k
    ImGuiContext& g = *GImGui;
5345
80.5k
    ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
5346
80.5k
    for (ImGuiViewportP* viewport : g.Viewports)
5347
80.5k
        virtual_space.Add(viewport->GetMainRect());
5348
80.5k
    g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();
5349
80.5k
    g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;
5350
80.5k
    g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);
5351
80.5k
    g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;
5352
80.5k
    if (g.Style.AntiAliasedLines)
5353
80.5k
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;
5354
80.5k
    if (g.Style.AntiAliasedLinesUseTex && !(g.IO.Fonts->Flags & ImFontAtlasFlags_NoBakedLines))
5355
80.5k
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;
5356
80.5k
    if (g.Style.AntiAliasedFill)
5357
80.5k
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;
5358
80.5k
    if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)
5359
80.5k
        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;
5360
80.5k
    g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments.
5361
80.5k
}
5362
5363
void ImGui::NewFrame()
5364
80.5k
{
5365
80.5k
    IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?");
5366
80.5k
    ImGuiContext& g = *GImGui;
5367
5368
    // Remove pending delete hooks before frame start.
5369
    // This deferred removal avoid issues of removal while iterating the hook vector
5370
80.5k
    for (int n = g.Hooks.Size - 1; n >= 0; n--)
5371
0
        if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)
5372
0
            g.Hooks.erase(&g.Hooks[n]);
5373
5374
80.5k
    CallContextHooks(&g, ImGuiContextHookType_NewFramePre);
5375
5376
    // Check and assert for various common IO and Configuration mistakes
5377
80.5k
    ErrorCheckNewFrameSanityChecks();
5378
5379
    // Load settings on first frame, save settings when modified (after a delay)
5380
80.5k
    UpdateSettings();
5381
5382
80.5k
    g.Time += g.IO.DeltaTime;
5383
80.5k
    g.FrameCount += 1;
5384
80.5k
    g.TooltipOverrideCount = 0;
5385
80.5k
    g.WindowsActiveCount = 0;
5386
80.5k
    g.MenusIdSubmittedThisFrame.resize(0);
5387
5388
    // Calculate frame-rate for the user, as a purely luxurious feature
5389
80.5k
    g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];
5390
80.5k
    g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;
5391
80.5k
    g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
5392
80.5k
    g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));
5393
80.5k
    g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;
5394
5395
    // Process input queue (trickle as many events as possible), turn events into writes to IO structure
5396
80.5k
    g.InputEventsTrail.resize(0);
5397
80.5k
    UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);
5398
5399
    // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)
5400
80.5k
    UpdateViewportsNewFrame();
5401
5402
    // Update texture list (collect destroyed textures, etc.)
5403
80.5k
    UpdateTexturesNewFrame();
5404
5405
    // Setup current font and draw list shared data
5406
80.5k
    SetupDrawListSharedData();
5407
80.5k
    UpdateFontsNewFrame();
5408
5409
80.5k
    g.WithinFrameScope = true;
5410
5411
    // Mark rendering data as invalid to prevent user who may have a handle on it to use it.
5412
80.5k
    for (ImGuiViewportP* viewport : g.Viewports)
5413
80.5k
        viewport->DrawDataP.Valid = false;
5414
5415
    // Drag and drop keep the source ID alive so even if the source disappear our state is consistent
5416
80.5k
    if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)
5417
0
        KeepAliveID(g.DragDropPayload.SourceId);
5418
5419
    // [DEBUG]
5420
80.5k
    if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL
5421
80.5k
        g.DebugDrawIdConflictsId = 0;
5422
80.5k
    if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1)
5423
0
        g.DebugDrawIdConflictsId = g.HoveredIdPreviousFrame;
5424
5425
    // Update HoveredId data
5426
80.5k
    if (!g.HoveredIdPreviousFrame)
5427
79.4k
        g.HoveredIdTimer = 0.0f;
5428
80.5k
    if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))
5429
79.4k
        g.HoveredIdNotActiveTimer = 0.0f;
5430
80.5k
    if (g.HoveredId)
5431
1.15k
        g.HoveredIdTimer += g.IO.DeltaTime;
5432
80.5k
    if (g.HoveredId && g.ActiveId != g.HoveredId)
5433
1.15k
        g.HoveredIdNotActiveTimer += g.IO.DeltaTime;
5434
80.5k
    g.HoveredIdPreviousFrame = g.HoveredId;
5435
80.5k
    g.HoveredIdPreviousFrameItemCount = 0;
5436
80.5k
    g.HoveredId = 0;
5437
80.5k
    g.HoveredIdAllowOverlap = false;
5438
80.5k
    g.HoveredIdIsDisabled = false;
5439
5440
    // Clear ActiveID if the item is not alive anymore.
5441
    // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().
5442
    // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.
5443
80.5k
    if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)
5444
0
    {
5445
0
        IMGUI_DEBUG_LOG_ACTIVEID("NewFrame(): ClearActiveID() because it isn't marked alive anymore!\n");
5446
0
        ClearActiveID();
5447
0
    }
5448
5449
    // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)
5450
80.5k
    if (g.ActiveId)
5451
0
        g.ActiveIdTimer += g.IO.DeltaTime;
5452
80.5k
    g.LastActiveIdTimer += g.IO.DeltaTime;
5453
80.5k
    g.ActiveIdPreviousFrame = g.ActiveId;
5454
80.5k
    g.ActiveIdIsAlive = 0;
5455
80.5k
    g.ActiveIdHasBeenEditedThisFrame = false;
5456
80.5k
    g.ActiveIdIsJustActivated = false;
5457
80.5k
    if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)
5458
0
        g.TempInputId = 0;
5459
80.5k
    if (g.ActiveId == 0)
5460
80.5k
    {
5461
80.5k
        g.ActiveIdUsingNavDirMask = 0x00;
5462
80.5k
        g.ActiveIdUsingAllKeyboardKeys = false;
5463
80.5k
    }
5464
80.5k
    if (g.DeactivatedItemData.ElapseFrame < g.FrameCount)
5465
80.5k
        g.DeactivatedItemData.ID = 0;
5466
80.5k
    g.DeactivatedItemData.IsAlive = false;
5467
5468
    // Record when we have been stationary as this state is preserved while over same item.
5469
    // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
5470
    // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
5471
80.5k
    if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5472
0
        g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
5473
80.5k
    else if (g.HoverItemDelayId == 0)
5474
80.5k
        g.HoverItemUnlockedStationaryId = 0;
5475
80.5k
    if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
5476
690
        g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
5477
79.9k
    else if (g.HoveredWindow == NULL)
5478
79.3k
        g.HoverWindowUnlockedStationaryId = 0;
5479
5480
    // Update hover delay for IsItemHovered() with delays and tooltips
5481
80.5k
    g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
5482
80.5k
    if (g.HoverItemDelayId != 0)
5483
0
    {
5484
0
        g.HoverItemDelayTimer += g.IO.DeltaTime;
5485
0
        g.HoverItemDelayClearTimer = 0.0f;
5486
0
        g.HoverItemDelayId = 0;
5487
0
    }
5488
80.5k
    else if (g.HoverItemDelayTimer > 0.0f)
5489
0
    {
5490
        // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
5491
        // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
5492
0
        g.HoverItemDelayClearTimer += g.IO.DeltaTime;
5493
0
        if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
5494
0
            g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
5495
0
    }
5496
5497
    // Drag and drop
5498
80.5k
    g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;
5499
80.5k
    g.DragDropAcceptIdCurr = 0;
5500
80.5k
    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
5501
80.5k
    g.DragDropWithinSource = false;
5502
80.5k
    g.DragDropWithinTarget = false;
5503
80.5k
    g.DragDropHoldJustPressedId = 0;
5504
80.5k
    g.TooltipPreviousWindow = NULL;
5505
5506
    // Close popups on focus lost (currently wip/opt-in)
5507
    //if (g.IO.AppFocusLost)
5508
    //    ClosePopupsExceptModals();
5509
5510
    // Update keyboard input state
5511
80.5k
    UpdateKeyboardInputs();
5512
5513
    //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));
5514
    //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));
5515
    //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));
5516
    //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));
5517
5518
    // Update keyboard/gamepad navigation
5519
80.5k
    NavUpdate();
5520
5521
    // Update mouse input state
5522
80.5k
    UpdateMouseInputs();
5523
5524
    // Mark all windows as not visible and compact unused memory.
5525
80.5k
    IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);
5526
80.5k
    const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;
5527
80.5k
    for (ImGuiWindow* window : g.Windows)
5528
240k
    {
5529
240k
        window->WasActive = window->Active;
5530
240k
        window->Active = false;
5531
240k
        window->WriteAccessed = false;
5532
240k
        window->BeginCountPreviousFrame = window->BeginCount;
5533
240k
        window->BeginCount = 0;
5534
5535
        // Garbage collect transient buffers of recently unused windows
5536
240k
        if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)
5537
1
            GcCompactTransientWindowBuffers(window);
5538
240k
    }
5539
5540
    // Find hovered window
5541
    // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)
5542
    // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active)
5543
80.5k
    UpdateHoveredWindowAndCaptureFlags(g.IO.MousePos);
5544
5545
    // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
5546
80.5k
    UpdateMouseMovingWindowNewFrame();
5547
5548
    // Background darkening/whitening
5549
80.5k
    if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
5550
0
        g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);
5551
80.5k
    else
5552
80.5k
        g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);
5553
5554
80.5k
    g.MouseCursor = ImGuiMouseCursor_Arrow;
5555
80.5k
    g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;
5556
5557
    // Platform IME data: reset for the frame
5558
80.5k
    g.PlatformImeDataPrev = g.PlatformImeData;
5559
80.5k
    g.PlatformImeData.WantVisible = g.PlatformImeData.WantTextInput = false;
5560
5561
    // Mouse wheel scrolling, scale
5562
80.5k
    UpdateMouseWheel();
5563
5564
    // Garbage collect transient buffers of recently unused tables
5565
80.5k
    for (int i = 0; i < g.TablesLastTimeActive.Size; i++)
5566
0
        if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)
5567
0
            TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));
5568
80.5k
    for (ImGuiTableTempData& table_temp_data : g.TablesTempData)
5569
0
        if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)
5570
0
            TableGcCompactTransientBuffers(&table_temp_data);
5571
80.5k
    if (g.GcCompactAll)
5572
0
        GcCompactTransientMiscBuffers();
5573
80.5k
    g.GcCompactAll = false;
5574
5575
    // Closing the focused window restore focus to the first active root window in descending z-order
5576
80.5k
    if (g.NavWindow && !g.NavWindow->WasActive)
5577
1
        FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);
5578
5579
    // No window should be open at the beginning of the frame.
5580
    // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.
5581
80.5k
    g.CurrentWindowStack.resize(0);
5582
80.5k
    g.BeginPopupStack.resize(0);
5583
80.5k
    g.ItemFlagsStack.resize(0);
5584
80.5k
    g.ItemFlagsStack.push_back(ImGuiItemFlags_AutoClosePopups); // Default flags
5585
80.5k
    g.CurrentItemFlags = g.ItemFlagsStack.back();
5586
80.5k
    g.GroupStack.resize(0);
5587
5588
    // [DEBUG] Update debug features
5589
80.5k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5590
80.5k
    UpdateDebugToolItemPicker();
5591
80.5k
    UpdateDebugToolStackQueries();
5592
80.5k
    UpdateDebugToolFlashStyleColor();
5593
80.5k
    if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
5594
0
    {
5595
0
        g.DebugLocateId = 0;
5596
0
        g.DebugBreakInLocateId = false;
5597
0
    }
5598
80.5k
    if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
5599
0
    {
5600
0
        DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
5601
0
        g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags;
5602
0
        g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None;
5603
0
    }
5604
80.5k
#endif
5605
5606
    // Create implicit/fallback window - which we will only render it if the user has added something to it.
5607
    // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
5608
    // This fallback is particularly important as it prevents ImGui:: calls from crashing.
5609
80.5k
    g.WithinFrameScopeWithImplicitWindow = true;
5610
80.5k
    SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
5611
80.5k
    Begin("Debug##Default");
5612
80.5k
    IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);
5613
5614
    // Store stack sizes
5615
80.5k
    g.ErrorCountCurrentFrame = 0;
5616
80.5k
    ErrorRecoveryStoreState(&g.StackSizesInNewFrame);
5617
5618
    // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,
5619
    // allowing to validate correct Begin/End behavior in user code.
5620
80.5k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5621
80.5k
    if (g.IO.ConfigDebugBeginReturnValueLoop)
5622
0
        g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);
5623
80.5k
    else
5624
80.5k
        g.DebugBeginReturnValueCullDepth = -1;
5625
80.5k
#endif
5626
5627
80.5k
    CallContextHooks(&g, ImGuiContextHookType_NewFramePost);
5628
80.5k
}
5629
5630
// FIXME: Add a more explicit sort order in the window structure.
5631
static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)
5632
0
{
5633
0
    const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;
5634
0
    const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;
5635
0
    if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))
5636
0
        return d;
5637
0
    if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))
5638
0
        return d;
5639
0
    return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);
5640
0
}
5641
5642
static void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)
5643
240k
{
5644
240k
    out_sorted_windows->push_back(window);
5645
240k
    if (window->Active)
5646
81.2k
    {
5647
81.2k
        int count = window->DC.ChildWindows.Size;
5648
81.2k
        ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);
5649
81.2k
        for (int i = 0; i < count; i++)
5650
0
        {
5651
0
            ImGuiWindow* child = window->DC.ChildWindows[i];
5652
0
            if (child->Active)
5653
0
                AddWindowToSortBuffer(out_sorted_windows, child);
5654
0
        }
5655
81.2k
    }
5656
240k
}
5657
5658
static void AddWindowToDrawData(ImGuiWindow* window, int layer)
5659
81.2k
{
5660
81.2k
    ImGuiContext& g = *GImGui;
5661
81.2k
    ImGuiViewportP* viewport = g.Viewports[0];
5662
81.2k
    g.IO.MetricsRenderWindows++;
5663
81.2k
    if (window->DrawList->_Splitter._Count > 1)
5664
0
        window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.
5665
81.2k
    ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);
5666
81.2k
    for (ImGuiWindow* child : window->DC.ChildWindows)
5667
0
        if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active
5668
0
            AddWindowToDrawData(child, layer);
5669
81.2k
}
5670
5671
static inline int GetWindowDisplayLayer(ImGuiWindow* window)
5672
81.2k
{
5673
81.2k
    return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;
5674
81.2k
}
5675
5676
// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)
5677
static inline void AddRootWindowToDrawData(ImGuiWindow* window)
5678
81.2k
{
5679
81.2k
    AddWindowToDrawData(window, GetWindowDisplayLayer(window));
5680
81.2k
}
5681
5682
static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)
5683
80.5k
{
5684
80.5k
    int n = builder->Layers[0]->Size;
5685
80.5k
    int full_size = n;
5686
161k
    for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)
5687
80.5k
        full_size += builder->Layers[i]->Size;
5688
80.5k
    builder->Layers[0]->resize(full_size);
5689
161k
    for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)
5690
80.5k
    {
5691
80.5k
        ImVector<ImDrawList*>* layer = builder->Layers[layer_n];
5692
80.5k
        if (layer->empty())
5693
80.5k
            continue;
5694
0
        memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));
5695
0
        n += layer->Size;
5696
0
        layer->resize(0);
5697
0
    }
5698
80.5k
}
5699
5700
static void InitViewportDrawData(ImGuiViewportP* viewport)
5701
80.5k
{
5702
80.5k
    ImGuiIO& io = ImGui::GetIO();
5703
80.5k
    ImDrawData* draw_data = &viewport->DrawDataP;
5704
5705
80.5k
    viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;
5706
80.5k
    viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;
5707
80.5k
    viewport->DrawDataBuilder.Layers[0]->resize(0);
5708
80.5k
    viewport->DrawDataBuilder.Layers[1]->resize(0);
5709
5710
80.5k
    draw_data->Valid = true;
5711
80.5k
    draw_data->CmdListsCount = 0;
5712
80.5k
    draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;
5713
80.5k
    draw_data->DisplayPos = viewport->Pos;
5714
80.5k
    draw_data->DisplaySize = viewport->Size;
5715
80.5k
    draw_data->FramebufferScale = io.DisplayFramebufferScale;
5716
80.5k
    draw_data->OwnerViewport = viewport;
5717
80.5k
    draw_data->Textures = &ImGui::GetPlatformIO().Textures;
5718
80.5k
}
5719
5720
// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.
5721
// - When using this function it is sane to ensure that float are perfectly rounded to integer values,
5722
//   so that e.g. (int)(max.x-min.x) in user's render produce correct result.
5723
// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():
5724
//   some frequently called functions which to modify both channels and clipping simultaneously tend to use the
5725
//   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.
5726
// - This is analogous to PushFont()/PopFont() in the sense that are a mixing a global stack and a window stack,
5727
//   which in the case of ClipRect is not so problematic but tends to be more restrictive for fonts.
5728
void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)
5729
404k
{
5730
404k
    ImGuiWindow* window = GetCurrentWindow();
5731
404k
    window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);
5732
404k
    window->ClipRect = window->DrawList->_ClipRectStack.back();
5733
404k
}
5734
5735
void ImGui::PopClipRect()
5736
242k
{
5737
242k
    ImGuiWindow* window = GetCurrentWindow();
5738
242k
    window->DrawList->PopClipRect();
5739
242k
    window->ClipRect = window->DrawList->_ClipRectStack.back();
5740
242k
}
5741
5742
static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)
5743
0
{
5744
0
    if ((col & IM_COL32_A_MASK) == 0)
5745
0
        return;
5746
5747
0
    ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport();
5748
0
    ImRect viewport_rect = viewport->GetMainRect();
5749
5750
    // Draw behind window by moving the draw command at the FRONT of the draw list
5751
0
    {
5752
        // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows,
5753
        // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing.
5754
        // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order.
5755
0
        ImDrawList* draw_list = window->RootWindow->DrawList;
5756
0
        if (draw_list->CmdBuffer.Size == 0)
5757
0
            draw_list->AddDrawCmd();
5758
0
        draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)
5759
0
        draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);
5760
0
        ImDrawCmd cmd = draw_list->CmdBuffer.back();
5761
0
        IM_ASSERT(cmd.ElemCount == 6);
5762
0
        draw_list->CmdBuffer.pop_back();
5763
0
        draw_list->CmdBuffer.push_front(cmd);
5764
0
        draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.
5765
0
        draw_list->PopClipRect();
5766
0
    }
5767
0
}
5768
5769
ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)
5770
0
{
5771
0
    ImGuiContext& g = *GImGui;
5772
0
    ImGuiWindow* bottom_most_visible_window = parent_window;
5773
0
    for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)
5774
0
    {
5775
0
        ImGuiWindow* window = g.Windows[i];
5776
0
        if (window->Flags & ImGuiWindowFlags_ChildWindow)
5777
0
            continue;
5778
0
        if (!IsWindowWithinBeginStackOf(window, parent_window))
5779
0
            break;
5780
0
        if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))
5781
0
            bottom_most_visible_window = window;
5782
0
    }
5783
0
    return bottom_most_visible_window;
5784
0
}
5785
5786
static void ImGui::RenderDimmedBackgrounds()
5787
80.5k
{
5788
80.5k
    ImGuiContext& g = *GImGui;
5789
80.5k
    ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();
5790
80.5k
    if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
5791
80.5k
        return;
5792
0
    const bool dim_bg_for_modal = (modal_window != NULL);
5793
0
    const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);
5794
0
    if (!dim_bg_for_modal && !dim_bg_for_window_list)
5795
0
        return;
5796
5797
0
    if (dim_bg_for_modal)
5798
0
    {
5799
        // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.
5800
0
        ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);
5801
0
        RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio));
5802
0
    }
5803
0
    else if (dim_bg_for_window_list)
5804
0
    {
5805
        // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window
5806
0
        RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));
5807
5808
        // Draw border around CTRL+Tab target window
5809
0
        ImGuiWindow* window = g.NavWindowingTargetAnim;
5810
0
        ImGuiViewport* viewport = GetMainViewport();
5811
0
        float distance = g.FontSize;
5812
0
        ImRect bb = window->Rect();
5813
0
        bb.Expand(distance);
5814
0
        if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)
5815
0
            bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward
5816
0
        if (window->DrawList->CmdBuffer.Size == 0)
5817
0
            window->DrawList->AddDrawCmd();
5818
0
        window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);
5819
0
        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); // FIXME-DPI
5820
0
        window->DrawList->PopClipRect();
5821
0
    }
5822
0
}
5823
5824
// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
5825
void ImGui::EndFrame()
5826
80.5k
{
5827
80.5k
    ImGuiContext& g = *GImGui;
5828
80.5k
    IM_ASSERT(g.Initialized);
5829
5830
    // Don't process EndFrame() multiple times.
5831
80.5k
    if (g.FrameCountEnded == g.FrameCount)
5832
0
        return;
5833
80.5k
    if (!g.WithinFrameScope)
5834
0
    {
5835
0
        IM_ASSERT_USER_ERROR(g.WithinFrameScope, "Forgot to call ImGui::NewFrame()?");
5836
0
        return;
5837
0
    }
5838
5839
80.5k
    CallContextHooks(&g, ImGuiContextHookType_EndFramePre);
5840
5841
    // [EXPERIMENTAL] Recover from errors
5842
80.5k
    if (g.IO.ConfigErrorRecovery)
5843
80.5k
        ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame);
5844
80.5k
    ErrorCheckEndFrameSanityChecks();
5845
80.5k
    ErrorCheckEndFrameFinalizeErrorTooltip();
5846
5847
    // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)
5848
80.5k
    ImGuiPlatformImeData* ime_data = &g.PlatformImeData;
5849
80.5k
    if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)
5850
0
    {
5851
0
        IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);
5852
0
        IM_ASSERT(ime_data->ViewportId == IMGUI_VIEWPORT_DEFAULT_ID); // master branch
5853
0
        ImGuiViewport* viewport = GetMainViewport();
5854
0
        g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data);
5855
0
    }
5856
80.5k
    g.WantTextInputNextFrame = ime_data->WantTextInput ? 1 : 0;
5857
5858
    // Hide implicit/fallback "Debug" window if it hasn't been used
5859
80.5k
    g.WithinFrameScopeWithImplicitWindow = false;
5860
80.5k
    if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)
5861
80.5k
        g.CurrentWindow->Active = false;
5862
80.5k
    End();
5863
5864
    // Update navigation: CTRL+Tab, wrap-around requests
5865
80.5k
    NavEndFrame();
5866
5867
    // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)
5868
80.5k
    if (g.DragDropActive)
5869
0
    {
5870
0
        bool is_delivered = g.DragDropPayload.Delivery;
5871
0
        bool is_elapsed = (g.DragDropSourceFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_PayloadAutoExpire) || g.DragDropMouseButton == -1 || !IsMouseDown(g.DragDropMouseButton));
5872
0
        if (is_delivered || is_elapsed)
5873
0
            ClearDragDrop();
5874
0
    }
5875
5876
    // Drag and Drop: Fallback for missing source tooltip. This is not ideal but better than nothing.
5877
    // If you want to handle source item disappearing: instead of submitting your description tooltip
5878
    // in the BeginDragDropSource() block of the dragged item, you can submit them from a safe single spot
5879
    // (e.g. end of your item loop, or before EndFrame) by reading payload data.
5880
    // In the typical case, the contents of drag tooltip should be possible to infer solely from payload data.
5881
80.5k
    if (g.DragDropActive && g.DragDropSourceFrameCount + 1 < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
5882
0
    {
5883
0
        g.DragDropWithinSource = true;
5884
0
        SetTooltip("...");
5885
0
        g.DragDropWithinSource = false;
5886
0
    }
5887
5888
    // End frame
5889
80.5k
    g.WithinFrameScope = false;
5890
80.5k
    g.FrameCountEnded = g.FrameCount;
5891
80.5k
    UpdateFontsEndFrame();
5892
5893
    // Initiate moving window + handle left-click and right-click focus
5894
80.5k
    UpdateMouseMovingWindowEndFrame();
5895
5896
    // Sort the window list so that all child windows are after their parent
5897
    // We cannot do that on FocusWindow() because children may not exist yet
5898
80.5k
    g.WindowsTempSortBuffer.resize(0);
5899
80.5k
    g.WindowsTempSortBuffer.reserve(g.Windows.Size);
5900
80.5k
    for (ImGuiWindow* window : g.Windows)
5901
240k
    {
5902
240k
        if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it
5903
0
            continue;
5904
240k
        AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);
5905
240k
    }
5906
5907
    // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.
5908
80.5k
    IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);
5909
80.5k
    g.Windows.swap(g.WindowsTempSortBuffer);
5910
80.5k
    g.IO.MetricsActiveWindows = g.WindowsActiveCount;
5911
5912
80.5k
    UpdateTexturesEndFrame();
5913
5914
    // Unlock font atlas
5915
80.5k
    for (ImFontAtlas* atlas : g.FontAtlases)
5916
80.5k
        atlas->Locked = false;
5917
5918
    // Clear Input data for next frame
5919
80.5k
    g.IO.MousePosPrev = g.IO.MousePos;
5920
80.5k
    g.IO.AppFocusLost = false;
5921
80.5k
    g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;
5922
80.5k
    g.IO.InputQueueCharacters.resize(0);
5923
5924
80.5k
    CallContextHooks(&g, ImGuiContextHookType_EndFramePost);
5925
80.5k
}
5926
5927
// Prepare the data for rendering so you can call GetDrawData()
5928
// (As with anything within the ImGui:: namespace this doesn't touch your GPU or graphics API at all:
5929
// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)
5930
void ImGui::Render()
5931
80.5k
{
5932
80.5k
    ImGuiContext& g = *GImGui;
5933
80.5k
    IM_ASSERT(g.Initialized);
5934
5935
80.5k
    if (g.FrameCountEnded != g.FrameCount)
5936
80.5k
        EndFrame();
5937
80.5k
    if (g.FrameCountRendered == g.FrameCount)
5938
0
        return;
5939
80.5k
    g.FrameCountRendered = g.FrameCount;
5940
5941
80.5k
    g.IO.MetricsRenderWindows = 0;
5942
80.5k
    CallContextHooks(&g, ImGuiContextHookType_RenderPre);
5943
5944
    // Add background ImDrawList (for each active viewport)
5945
80.5k
    for (ImGuiViewportP* viewport : g.Viewports)
5946
80.5k
    {
5947
80.5k
        InitViewportDrawData(viewport);
5948
80.5k
        if (viewport->BgFgDrawLists[0] != NULL)
5949
0
            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));
5950
80.5k
    }
5951
5952
    // Draw modal/window whitening backgrounds
5953
80.5k
    RenderDimmedBackgrounds();
5954
5955
    // Add ImDrawList to render
5956
80.5k
    ImGuiWindow* windows_to_render_top_most[2];
5957
80.5k
    windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;
5958
80.5k
    windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);
5959
80.5k
    for (ImGuiWindow* window : g.Windows)
5960
240k
    {
5961
240k
        IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
5962
240k
        if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])
5963
81.2k
            AddRootWindowToDrawData(window);
5964
240k
    }
5965
241k
    for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)
5966
161k
        if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window
5967
0
            AddRootWindowToDrawData(windows_to_render_top_most[n]);
5968
5969
    // Draw software mouse cursor if requested by io.MouseDrawCursor flag
5970
80.5k
    if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)
5971
0
        RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));
5972
5973
    // Setup ImDrawData structures for end-user
5974
80.5k
    g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;
5975
80.5k
    for (ImGuiViewportP* viewport : g.Viewports)
5976
80.5k
    {
5977
80.5k
        FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);
5978
5979
        // Add foreground ImDrawList (for each active viewport)
5980
80.5k
        if (viewport->BgFgDrawLists[1] != NULL)
5981
0
            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));
5982
5983
        // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).
5984
80.5k
        ImDrawData* draw_data = &viewport->DrawDataP;
5985
80.5k
        IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);
5986
80.5k
        for (ImDrawList* draw_list : draw_data->CmdLists)
5987
81.2k
            draw_list->_PopUnusedDrawCmd();
5988
5989
80.5k
        g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;
5990
80.5k
        g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;
5991
80.5k
    }
5992
5993
80.5k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5994
80.5k
    if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
5995
80.5k
        for (ImFontAtlas* atlas : g.FontAtlases)
5996
80.5k
            ImFontAtlasDebugLogTextureRequests(atlas);
5997
80.5k
#endif
5998
5999
80.5k
    CallContextHooks(&g, ImGuiContextHookType_RenderPost);
6000
80.5k
}
6001
6002
// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.
6003
// CalcTextSize("") should return ImVec2(0.0f, g.FontSize)
6004
ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)
6005
568k
{
6006
568k
    ImGuiContext& g = *GImGui;
6007
6008
568k
    const char* text_display_end;
6009
568k
    if (hide_text_after_double_hash)
6010
568k
        text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string
6011
0
    else
6012
0
        text_display_end = text_end;
6013
6014
568k
    ImFont* font = g.Font;
6015
568k
    const float font_size = g.FontSize;
6016
568k
    if (text == text_display_end)
6017
243k
        return ImVec2(0.0f, font_size);
6018
324k
    ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);
6019
6020
    // Round
6021
    // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.
6022
    // FIXME: Investigate using ceilf or e.g.
6023
    // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
6024
    // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html
6025
324k
    text_size.x = IM_TRUNC(text_size.x + 0.99999f);
6026
6027
324k
    return text_size;
6028
568k
}
6029
6030
// Find window given position, search front-to-back
6031
// - Typically write output back to g.HoveredWindow and g.HoveredWindowUnderMovingWindow.
6032
// - FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically
6033
//   with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is
6034
//   called, aka before the next Begin(). Moving window isn't affected.
6035
// - The 'find_first_and_in_any_viewport = true' mode is only used by TestEngine. It is simpler to maintain here.
6036
void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window)
6037
80.5k
{
6038
80.5k
    ImGuiContext& g = *GImGui;
6039
80.5k
    ImGuiWindow* hovered_window = NULL;
6040
80.5k
    ImGuiWindow* hovered_window_under_moving_window = NULL;
6041
6042
80.5k
    if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))
6043
0
        hovered_window = g.MovingWindow;
6044
6045
80.5k
    ImVec2 padding_regular = g.Style.TouchExtraPadding;
6046
80.5k
    ImVec2 padding_for_resize = ImMax(g.Style.TouchExtraPadding, ImVec2(g.Style.WindowBorderHoverPadding, g.Style.WindowBorderHoverPadding));
6047
318k
    for (int i = g.Windows.Size - 1; i >= 0; i--)
6048
238k
    {
6049
238k
        ImGuiWindow* window = g.Windows[i];
6050
238k
        IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6051
238k
        if (!window->WasActive || window->Hidden)
6052
157k
            continue;
6053
80.8k
        if (window->Flags & ImGuiWindowFlags_NoMouseInputs)
6054
0
            continue;
6055
6056
        // Using the clipped AABB, a child window will typically be clipped by its parent (not always)
6057
80.8k
        ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;
6058
80.8k
        if (!window->OuterRectClipped.ContainsWithPad(pos, hit_padding))
6059
79.6k
            continue;
6060
6061
        // Support for one rectangular hole in any given window
6062
        // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)
6063
1.19k
        if (window->HitTestHoleSize.x != 0)
6064
0
        {
6065
0
            ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);
6066
0
            ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);
6067
0
            if (ImRect(hole_pos, hole_pos + hole_size).Contains(pos))
6068
0
                continue;
6069
0
        }
6070
6071
1.19k
        if (find_first_and_in_any_viewport)
6072
0
        {
6073
0
            hovered_window = window;
6074
0
            break;
6075
0
        }
6076
1.19k
        else
6077
1.19k
        {
6078
1.19k
            if (hovered_window == NULL)
6079
1.19k
                hovered_window = window;
6080
1.19k
            IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.
6081
1.19k
            if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
6082
1.19k
                hovered_window_under_moving_window = window;
6083
1.19k
            if (hovered_window && hovered_window_under_moving_window)
6084
1.19k
                break;
6085
1.19k
        }
6086
1.19k
    }
6087
6088
80.5k
    *out_hovered_window = hovered_window;
6089
80.5k
    if (out_hovered_window_under_moving_window != NULL)
6090
80.5k
        *out_hovered_window_under_moving_window = hovered_window_under_moving_window;
6091
80.5k
}
6092
6093
bool ImGui::IsItemActive()
6094
80.5k
{
6095
80.5k
    ImGuiContext& g = *GImGui;
6096
80.5k
    if (g.ActiveId)
6097
0
        return g.ActiveId == g.LastItemData.ID;
6098
80.5k
    return false;
6099
80.5k
}
6100
6101
bool ImGui::IsItemActivated()
6102
0
{
6103
0
    ImGuiContext& g = *GImGui;
6104
0
    if (g.ActiveId)
6105
0
        if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)
6106
0
            return true;
6107
0
    return false;
6108
0
}
6109
6110
bool ImGui::IsItemDeactivated()
6111
0
{
6112
0
    ImGuiContext& g = *GImGui;
6113
0
    if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)
6114
0
        return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;
6115
0
    return (g.DeactivatedItemData.ID == g.LastItemData.ID && g.LastItemData.ID != 0 && g.DeactivatedItemData.ElapseFrame >= g.FrameCount);
6116
0
}
6117
6118
bool ImGui::IsItemDeactivatedAfterEdit()
6119
0
{
6120
0
    ImGuiContext& g = *GImGui;
6121
0
    return IsItemDeactivated() && g.DeactivatedItemData.HasBeenEditedBefore;
6122
0
}
6123
6124
// == (GetItemID() == GetFocusID() && GetFocusID() != 0)
6125
bool ImGui::IsItemFocused()
6126
0
{
6127
0
    ImGuiContext& g = *GImGui;
6128
0
    return g.NavId == g.LastItemData.ID && g.NavId != 0;
6129
0
}
6130
6131
// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!
6132
// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.
6133
bool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)
6134
0
{
6135
0
    return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);
6136
0
}
6137
6138
bool ImGui::IsItemToggledOpen()
6139
0
{
6140
0
    ImGuiContext& g = *GImGui;
6141
0
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;
6142
0
}
6143
6144
// Call after a Selectable() or TreeNode() involved in multi-selection.
6145
// Useful if you need the per-item information before reaching EndMultiSelect(), e.g. for rendering purpose.
6146
// This is only meant to be called inside a BeginMultiSelect()/EndMultiSelect() block.
6147
// (Outside of multi-select, it would be misleading/ambiguous to report this signal, as widgets
6148
// return e.g. a pressed event and user code is in charge of altering selection in ways we cannot predict.)
6149
bool ImGui::IsItemToggledSelection()
6150
0
{
6151
0
    ImGuiContext& g = *GImGui;
6152
0
    IM_ASSERT(g.CurrentMultiSelect != NULL); // Can only be used inside a BeginMultiSelect()/EndMultiSelect()
6153
0
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;
6154
0
}
6155
6156
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
6157
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
6158
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
6159
bool ImGui::IsAnyItemHovered()
6160
0
{
6161
0
    ImGuiContext& g = *GImGui;
6162
0
    return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;
6163
0
}
6164
6165
bool ImGui::IsAnyItemActive()
6166
0
{
6167
0
    ImGuiContext& g = *GImGui;
6168
0
    return g.ActiveId != 0;
6169
0
}
6170
6171
bool ImGui::IsAnyItemFocused()
6172
0
{
6173
0
    ImGuiContext& g = *GImGui;
6174
0
    return g.NavId != 0 && g.NavCursorVisible;
6175
0
}
6176
6177
bool ImGui::IsItemVisible()
6178
0
{
6179
0
    ImGuiContext& g = *GImGui;
6180
0
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;
6181
0
}
6182
6183
bool ImGui::IsItemEdited()
6184
0
{
6185
0
    ImGuiContext& g = *GImGui;
6186
0
    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;
6187
0
}
6188
6189
// Allow next item to be overlapped by subsequent items.
6190
// This works by requiring HoveredId to match for two subsequent frames,
6191
// so if a following items overwrite it our interactions will naturally be disabled.
6192
void ImGui::SetNextItemAllowOverlap()
6193
0
{
6194
0
    ImGuiContext& g = *GImGui;
6195
0
    g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;
6196
0
}
6197
6198
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6199
// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.
6200
// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.
6201
void ImGui::SetItemAllowOverlap()
6202
0
{
6203
0
    ImGuiContext& g = *GImGui;
6204
0
    ImGuiID id = g.LastItemData.ID;
6205
0
    if (g.HoveredId == id)
6206
0
        g.HoveredIdAllowOverlap = true;
6207
0
    if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.
6208
0
        g.ActiveIdAllowOverlap = true;
6209
0
}
6210
#endif
6211
6212
// This is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations.
6213
// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version if needed?
6214
void ImGui::SetActiveIdUsingAllKeyboardKeys()
6215
0
{
6216
0
    ImGuiContext& g = *GImGui;
6217
0
    IM_ASSERT(g.ActiveId != 0);
6218
0
    g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;
6219
0
    g.ActiveIdUsingAllKeyboardKeys = true;
6220
0
    NavMoveRequestCancel();
6221
0
}
6222
6223
ImGuiID ImGui::GetItemID()
6224
0
{
6225
0
    ImGuiContext& g = *GImGui;
6226
0
    return g.LastItemData.ID;
6227
0
}
6228
6229
ImVec2 ImGui::GetItemRectMin()
6230
0
{
6231
0
    ImGuiContext& g = *GImGui;
6232
0
    return g.LastItemData.Rect.Min;
6233
0
}
6234
6235
ImVec2 ImGui::GetItemRectMax()
6236
0
{
6237
0
    ImGuiContext& g = *GImGui;
6238
0
    return g.LastItemData.Rect.Max;
6239
0
}
6240
6241
ImVec2 ImGui::GetItemRectSize()
6242
0
{
6243
0
    ImGuiContext& g = *GImGui;
6244
0
    return g.LastItemData.Rect.GetSize();
6245
0
}
6246
6247
// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.
6248
// ImGuiChildFlags_Borders is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details!
6249
bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6250
0
{
6251
0
    ImGuiID id = GetCurrentWindow()->GetID(str_id);
6252
0
    return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);
6253
0
}
6254
6255
bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6256
0
{
6257
0
    return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);
6258
0
}
6259
6260
bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)
6261
0
{
6262
0
    ImGuiContext& g = *GImGui;
6263
0
    ImGuiWindow* parent_window = g.CurrentWindow;
6264
0
    IM_ASSERT(id != 0);
6265
6266
    // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.
6267
0
    const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened;
6268
0
    IM_UNUSED(ImGuiChildFlags_SupportedMask_);
6269
0
    IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?");
6270
0
    IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!");
6271
0
    if (child_flags & ImGuiChildFlags_AlwaysAutoResize)
6272
0
    {
6273
0
        IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6274
0
        IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!");
6275
0
    }
6276
0
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
6277
0
    if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)
6278
0
        child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;
6279
0
    if (window_flags & ImGuiWindowFlags_NavFlattened)
6280
0
        child_flags |= ImGuiChildFlags_NavFlattened;
6281
0
#endif
6282
0
    if (child_flags & ImGuiChildFlags_AutoResizeX)
6283
0
        child_flags &= ~ImGuiChildFlags_ResizeX;
6284
0
    if (child_flags & ImGuiChildFlags_AutoResizeY)
6285
0
        child_flags &= ~ImGuiChildFlags_ResizeY;
6286
6287
    // Set window flags
6288
0
    window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;
6289
0
    window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
6290
0
    if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))
6291
0
        window_flags |= ImGuiWindowFlags_AlwaysAutoResize;
6292
0
    if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6293
0
        window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
6294
6295
    // Special framed style
6296
0
    if (child_flags & ImGuiChildFlags_FrameStyle)
6297
0
    {
6298
0
        PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);
6299
0
        PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);
6300
0
        PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);
6301
0
        PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);
6302
0
        child_flags |= ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding;
6303
0
        window_flags |= ImGuiWindowFlags_NoMove;
6304
0
    }
6305
6306
    // Forward size
6307
    // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.
6308
    // (the alternative would to store conditional flags per axis, which is possible but more code)
6309
0
    const ImVec2 size_avail = GetContentRegionAvail();
6310
0
    const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);
6311
0
    ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);
6312
6313
    // A SetNextWindowSize() call always has priority (#8020)
6314
    // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now)
6315
    // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer.
6316
0
    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0)
6317
0
    {
6318
0
        if (g.NextWindowData.SizeVal.x > 0.0f)
6319
0
        {
6320
0
            size.x = g.NextWindowData.SizeVal.x;
6321
0
            child_flags &= ~ImGuiChildFlags_ResizeX;
6322
0
        }
6323
0
        if (g.NextWindowData.SizeVal.y > 0.0f)
6324
0
        {
6325
0
            size.y = g.NextWindowData.SizeVal.y;
6326
0
            child_flags &= ~ImGuiChildFlags_ResizeY;
6327
0
        }
6328
0
    }
6329
0
    SetNextWindowSize(size);
6330
6331
    // Forward child flags (we allow prior settings to merge but it'll only work for adding flags)
6332
0
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags)
6333
0
        g.NextWindowData.ChildFlags |= child_flags;
6334
0
    else
6335
0
        g.NextWindowData.ChildFlags = child_flags;
6336
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags;
6337
6338
    // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.
6339
    // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.
6340
    // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.
6341
0
    const char* temp_window_name;
6342
    /*if (name && parent_window->IDStack.back() == parent_window->ID)
6343
        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack
6344
    else*/
6345
0
    if (name)
6346
0
        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id);
6347
0
    else
6348
0
        ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id);
6349
6350
    // Set style
6351
0
    const float backup_border_size = g.Style.ChildBorderSize;
6352
0
    if ((child_flags & ImGuiChildFlags_Borders) == 0)
6353
0
        g.Style.ChildBorderSize = 0.0f;
6354
6355
    // Begin into window
6356
0
    const bool ret = Begin(temp_window_name, NULL, window_flags);
6357
6358
    // Restore style
6359
0
    g.Style.ChildBorderSize = backup_border_size;
6360
0
    if (child_flags & ImGuiChildFlags_FrameStyle)
6361
0
    {
6362
0
        PopStyleVar(3);
6363
0
        PopStyleColor();
6364
0
    }
6365
6366
0
    ImGuiWindow* child_window = g.CurrentWindow;
6367
0
    child_window->ChildId = id;
6368
6369
    // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.
6370
    // While this is not really documented/defined, it seems that the expected thing to do.
6371
0
    if (child_window->BeginCount == 1)
6372
0
        parent_window->DC.CursorPos = child_window->Pos;
6373
6374
    // Process navigation-in immediately so NavInit can run on first frame
6375
    // Can enter a child if (A) it has navigable items or (B) it can be scrolled.
6376
0
    const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id);
6377
0
    if (g.ActiveId == temp_id_for_activation)
6378
0
        ClearActiveID();
6379
0
    if (g.NavActivateId == id && !(child_flags & ImGuiChildFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))
6380
0
    {
6381
0
        FocusWindow(child_window);
6382
0
        NavInitWindow(child_window, false);
6383
0
        SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item
6384
0
        g.ActiveIdSource = g.NavInputSource;
6385
0
    }
6386
0
    return ret;
6387
0
}
6388
6389
void ImGui::EndChild()
6390
0
{
6391
0
    ImGuiContext& g = *GImGui;
6392
0
    ImGuiWindow* child_window = g.CurrentWindow;
6393
6394
0
    const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
6395
0
    IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls
6396
6397
0
    g.WithinEndChildID = child_window->ID;
6398
0
    ImVec2 child_size = child_window->Size;
6399
0
    End();
6400
0
    if (child_window->BeginCount == 1)
6401
0
    {
6402
0
        ImGuiWindow* parent_window = g.CurrentWindow;
6403
0
        ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);
6404
0
        ItemSize(child_size);
6405
0
        const bool nav_flattened = (child_window->ChildFlags & ImGuiChildFlags_NavFlattened) != 0;
6406
0
        if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !nav_flattened)
6407
0
        {
6408
0
            ItemAdd(bb, child_window->ChildId);
6409
0
            RenderNavCursor(bb, child_window->ChildId);
6410
6411
            // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)
6412
0
            if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)
6413
0
                RenderNavCursor(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavRenderCursorFlags_Compact);
6414
0
        }
6415
0
        else
6416
0
        {
6417
            // Not navigable into
6418
            // - This is a bit of a fringe use case, mostly useful for undecorated, non-scrolling contents childs, or empty childs.
6419
            // - We could later decide to not apply this path if ImGuiChildFlags_FrameStyle or ImGuiChildFlags_Borders is set.
6420
0
            ItemAdd(bb, child_window->ChildId, NULL, ImGuiItemFlags_NoNav);
6421
6422
            // But when flattened we directly reach items, adjust active layer mask accordingly
6423
0
            if (nav_flattened)
6424
0
                parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;
6425
0
        }
6426
0
        if (g.HoveredWindow == child_window)
6427
0
            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
6428
0
        child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags;
6429
        //SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd()
6430
0
    }
6431
0
    else
6432
0
    {
6433
0
        SetLastItemDataForChildWindowItem(child_window, child_window->Rect());
6434
0
    }
6435
6436
0
    g.WithinEndChildID = backup_within_end_child_id;
6437
0
    g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
6438
0
}
6439
6440
static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)
6441
7
{
6442
7
    window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);
6443
7
    window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);
6444
7
    window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
6445
7
}
6446
6447
ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
6448
242k
{
6449
242k
    ImGuiContext& g = *GImGui;
6450
242k
    return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
6451
242k
}
6452
6453
ImGuiWindow* ImGui::FindWindowByName(const char* name)
6454
242k
{
6455
242k
    ImGuiID id = ImHashStr(name);
6456
242k
    return FindWindowByID(id);
6457
242k
}
6458
6459
static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6460
1
{
6461
1
    window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y));
6462
1
    if (settings->Size.x > 0 && settings->Size.y > 0)
6463
1
        window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));
6464
1
    window->Collapsed = settings->Collapsed;
6465
1
}
6466
6467
static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)
6468
3
{
6469
    // Initial window state with e.g. default/arbitrary window position
6470
    // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.
6471
3
    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
6472
3
    window->Pos = main_viewport->Pos + ImVec2(60, 60);
6473
3
    window->Size = window->SizeFull = ImVec2(0, 0);
6474
3
    window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
6475
6476
3
    if (settings != NULL)
6477
1
    {
6478
1
        SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);
6479
1
        ApplyWindowSettings(window, settings);
6480
1
    }
6481
3
    window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values
6482
6483
3
    if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)
6484
1
    {
6485
1
        window->AutoFitFramesX = window->AutoFitFramesY = 2;
6486
1
        window->AutoFitOnlyGrows = false;
6487
1
    }
6488
2
    else
6489
2
    {
6490
2
        if (window->Size.x <= 0.0f)
6491
1
            window->AutoFitFramesX = 2;
6492
2
        if (window->Size.y <= 0.0f)
6493
1
            window->AutoFitFramesY = 2;
6494
2
        window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);
6495
2
    }
6496
3
}
6497
6498
static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)
6499
3
{
6500
    // Create window the first time
6501
    //IMGUI_DEBUG_LOG("CreateNewWindow '%s', flags = 0x%08X\n", name, flags);
6502
3
    ImGuiContext& g = *GImGui;
6503
3
    ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);
6504
3
    window->Flags = flags;
6505
3
    g.WindowsById.SetVoidPtr(window->ID, window);
6506
6507
3
    ImGuiWindowSettings* settings = NULL;
6508
3
    if (!(flags & ImGuiWindowFlags_NoSavedSettings))
6509
1
        if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)
6510
1
            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
6511
6512
3
    InitOrLoadWindowSettings(window, settings);
6513
6514
3
    if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)
6515
0
        g.Windows.push_front(window); // Quite slow but rare and only once
6516
3
    else
6517
3
        g.Windows.push_back(window);
6518
6519
3
    return window;
6520
3
}
6521
6522
static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)
6523
485k
{
6524
    // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)
6525
    // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller.
6526
    // Perhaps should tend further a neater test for this.
6527
485k
    ImGuiContext& g = *GImGui;
6528
485k
    ImVec2 size_min;
6529
485k
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup))
6530
0
    {
6531
0
        size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;
6532
0
        size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;
6533
0
    }
6534
485k
    else
6535
485k
    {
6536
485k
        size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;
6537
485k
        size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f;
6538
485k
    }
6539
6540
    // Reduce artifacts with very small windows
6541
485k
    ImGuiWindow* window_for_height = window;
6542
485k
    size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f));
6543
485k
    return size_min;
6544
485k
}
6545
6546
static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)
6547
323k
{
6548
323k
    ImGuiContext& g = *GImGui;
6549
323k
    ImVec2 new_size = size_desired;
6550
323k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)
6551
0
    {
6552
        // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.
6553
0
        ImRect cr = g.NextWindowData.SizeConstraintRect;
6554
0
        new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;
6555
0
        new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;
6556
0
        if (g.NextWindowData.SizeCallback)
6557
0
        {
6558
0
            ImGuiSizeCallbackData data;
6559
0
            data.UserData = g.NextWindowData.SizeCallbackUserData;
6560
0
            data.Pos = window->Pos;
6561
0
            data.CurrentSize = window->SizeFull;
6562
0
            data.DesiredSize = new_size;
6563
0
            g.NextWindowData.SizeCallback(&data);
6564
0
            new_size = data.DesiredSize;
6565
0
        }
6566
0
        new_size.x = IM_TRUNC(new_size.x);
6567
0
        new_size.y = IM_TRUNC(new_size.y);
6568
0
    }
6569
6570
    // Minimum size
6571
323k
    ImVec2 size_min = CalcWindowMinSize(window);
6572
323k
    return ImMax(new_size, size_min);
6573
323k
}
6574
6575
static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)
6576
161k
{
6577
161k
    bool preserve_old_content_sizes = false;
6578
161k
    if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
6579
0
        preserve_old_content_sizes = true;
6580
161k
    else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)
6581
0
        preserve_old_content_sizes = true;
6582
161k
    if (preserve_old_content_sizes)
6583
0
    {
6584
0
        *content_size_current = window->ContentSize;
6585
0
        *content_size_ideal = window->ContentSizeIdeal;
6586
0
        return;
6587
0
    }
6588
6589
161k
    content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);
6590
161k
    content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);
6591
161k
    content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : ImTrunc64(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);
6592
161k
    content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : ImTrunc64(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);
6593
161k
}
6594
6595
static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)
6596
161k
{
6597
161k
    ImGuiContext& g = *GImGui;
6598
161k
    ImGuiStyle& style = g.Style;
6599
161k
    const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;
6600
161k
    const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;
6601
161k
    ImVec2 size_pad = window->WindowPadding * 2.0f;
6602
161k
    ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);
6603
6604
    // Determine maximum window size
6605
    // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction
6606
161k
    ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f;
6607
6608
161k
    if (window->Flags & ImGuiWindowFlags_Tooltip)
6609
0
    {
6610
        // Tooltip always resize (up to maximum size)
6611
0
        return ImMin(size_desired, size_max);
6612
0
    }
6613
161k
    else
6614
161k
    {
6615
161k
        ImVec2 size_min = CalcWindowMinSize(window);
6616
161k
        ImVec2 size_auto_fit = ImClamp(size_desired, ImMin(size_min, size_max), size_max);
6617
6618
        // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars,
6619
        // we may need to compute/store three variants of size_auto_fit, for x/y/xy.
6620
        // Here we implement a workaround for child windows only, but a full solution would apply to normal windows as well:
6621
161k
        if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && !(window->ChildFlags & (ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeY)))
6622
0
            size_auto_fit.y = window->SizeFull.y;
6623
161k
        else if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && !(window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_AutoResizeX)))
6624
0
            size_auto_fit.x = window->SizeFull.x;
6625
6626
        // When the window cannot fit all contents (either because of constraints, either because screen is too small),
6627
        // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.
6628
161k
        ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6629
161k
        bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);
6630
161k
        bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);
6631
161k
        if (will_have_scrollbar_x)
6632
0
            size_auto_fit.y += style.ScrollbarSize;
6633
161k
        if (will_have_scrollbar_y)
6634
0
            size_auto_fit.x += style.ScrollbarSize;
6635
161k
        return size_auto_fit;
6636
161k
    }
6637
161k
}
6638
6639
ImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)
6640
0
{
6641
0
    ImVec2 size_contents_current;
6642
0
    ImVec2 size_contents_ideal;
6643
0
    CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);
6644
0
    ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);
6645
0
    ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6646
0
    return size_final;
6647
0
}
6648
6649
static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)
6650
161k
{
6651
161k
    if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))
6652
660
        return ImGuiCol_PopupBg;
6653
161k
    if (window->Flags & ImGuiWindowFlags_ChildWindow)
6654
0
        return ImGuiCol_ChildBg;
6655
161k
    return ImGuiCol_WindowBg;
6656
161k
}
6657
6658
static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target_arg, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)
6659
0
{
6660
0
    ImVec2 corner_target = corner_target_arg;
6661
0
    if (window->Flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent
6662
0
    {
6663
0
        ImGuiWindow* parent_window = window->ParentWindow;
6664
0
        ImGuiWindowFlags parent_flags = parent_window->Flags;
6665
0
        ImRect limit_rect = parent_window->InnerRect;
6666
0
        limit_rect.Expand(ImVec2(-ImMax(parent_window->WindowPadding.x, parent_window->WindowBorderSize), -ImMax(parent_window->WindowPadding.y, parent_window->WindowBorderSize)));
6667
0
        if ((parent_flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (parent_flags & ImGuiWindowFlags_NoScrollbar))
6668
0
            corner_target.x = ImClamp(corner_target.x, limit_rect.Min.x, limit_rect.Max.x);
6669
0
        if (parent_flags & ImGuiWindowFlags_NoScrollbar)
6670
0
            corner_target.y = ImClamp(corner_target.y, limit_rect.Min.y, limit_rect.Max.y);
6671
0
    }
6672
0
    ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left
6673
0
    ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right
6674
0
    ImVec2 size_expected = pos_max - pos_min;
6675
0
    ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);
6676
0
    *out_pos = pos_min;
6677
0
    if (corner_norm.x == 0.0f)
6678
0
        out_pos->x -= (size_constrained.x - size_expected.x);
6679
0
    if (corner_norm.y == 0.0f)
6680
0
        out_pos->y -= (size_constrained.y - size_expected.y);
6681
0
    *out_size = size_constrained;
6682
0
}
6683
6684
// Data for resizing from resize grip / corner
6685
struct ImGuiResizeGripDef
6686
{
6687
    ImVec2  CornerPosN;
6688
    ImVec2  InnerDir;
6689
    int     AngleMin12, AngleMax12;
6690
};
6691
static const ImGuiResizeGripDef resize_grip_def[4] =
6692
{
6693
    { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right
6694
    { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left
6695
    { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)
6696
    { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)
6697
};
6698
6699
// Data for resizing from borders
6700
struct ImGuiResizeBorderDef
6701
{
6702
    ImVec2  InnerDir;               // Normal toward inside
6703
    ImVec2  SegmentN1, SegmentN2;   // End positions, normalized (0,0: upper left)
6704
    float   OuterAngle;             // Angle toward outside
6705
};
6706
static const ImGuiResizeBorderDef resize_border_def[4] =
6707
{
6708
    { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left
6709
    { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right
6710
    { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up
6711
    { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down
6712
};
6713
6714
static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)
6715
0
{
6716
0
    ImRect rect = window->Rect();
6717
0
    if (thickness == 0.0f)
6718
0
        rect.Max -= ImVec2(1, 1);
6719
0
    if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }
6720
0
    if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }
6721
0
    if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }
6722
0
    if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }
6723
0
    IM_ASSERT(0);
6724
0
    return ImRect();
6725
0
}
6726
6727
// 0..3: corners (Lower-right, Lower-left, Unused, Unused)
6728
ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)
6729
0
{
6730
0
    IM_ASSERT(n >= 0 && n < 4);
6731
0
    ImGuiID id = window->ID;
6732
0
    id = ImHashStr("#RESIZE", 0, id);
6733
0
    id = ImHashData(&n, sizeof(int), id);
6734
0
    return id;
6735
0
}
6736
6737
// Borders (Left, Right, Up, Down)
6738
ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)
6739
0
{
6740
0
    IM_ASSERT(dir >= 0 && dir < 4);
6741
0
    int n = (int)dir + 4;
6742
0
    ImGuiID id = window->ID;
6743
0
    id = ImHashStr("#RESIZE", 0, id);
6744
0
    id = ImHashData(&n, sizeof(int), id);
6745
0
    return id;
6746
0
}
6747
6748
// Handle resize for: Resize Grips, Borders, Gamepad
6749
// Return true when using auto-fit (double-click on resize grip)
6750
static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)
6751
161k
{
6752
161k
    ImGuiContext& g = *GImGui;
6753
161k
    ImGuiWindowFlags flags = window->Flags;
6754
6755
161k
    if ((flags & ImGuiWindowFlags_NoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
6756
80.5k
        return false;
6757
81.2k
    if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && (window->ChildFlags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)
6758
658
        return false;
6759
80.5k
    if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.
6760
80.5k
        return false;
6761
6762
0
    int ret_auto_fit_mask = 0x00;
6763
0
    const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
6764
0
    const float grip_hover_inner_size = (resize_grip_count > 0) ? IM_TRUNC(grip_draw_size * 0.75f) : 0.0f;
6765
0
    const float grip_hover_outer_size = g.WindowsBorderHoverPadding;
6766
6767
0
    ImRect clamp_rect = visibility_rect;
6768
0
    const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);
6769
0
    if (window_move_from_title_bar)
6770
0
        clamp_rect.Min.y -= window->TitleBarHeight;
6771
6772
0
    ImVec2 pos_target(FLT_MAX, FLT_MAX);
6773
0
    ImVec2 size_target(FLT_MAX, FLT_MAX);
6774
6775
    // Resize grips and borders are on layer 1
6776
0
    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
6777
6778
    // Manual resize grips
6779
0
    PushID("#RESIZE");
6780
0
    for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
6781
0
    {
6782
0
        const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];
6783
0
        const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);
6784
6785
        // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window
6786
0
        bool hovered, held;
6787
0
        ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);
6788
0
        if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
6789
0
        if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
6790
0
        ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()
6791
0
        ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);
6792
0
        ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6793
        //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));
6794
0
        if (hovered || held)
6795
0
            SetMouseCursor((resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE);
6796
6797
0
        if (held && g.IO.MouseDoubleClicked[0])
6798
0
        {
6799
            // Auto-fit when double-clicking
6800
0
            size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);
6801
0
            ret_auto_fit_mask = 0x03; // Both axes
6802
0
            ClearActiveID();
6803
0
        }
6804
0
        else if (held)
6805
0
        {
6806
            // Resize from any of the four corners
6807
            // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position
6808
0
            ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX);
6809
0
            ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX);
6810
0
            ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip
6811
0
            corner_target = ImClamp(corner_target, clamp_min, clamp_max);
6812
0
            CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);
6813
0
        }
6814
6815
        // Only lower-left grip is visible before hovering/activating
6816
0
        const bool resize_grip_visible = held || hovered || (resize_grip_n == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0);
6817
0
        if (resize_grip_visible)
6818
0
            resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);
6819
0
    }
6820
6821
0
    int resize_border_mask = 0x00;
6822
0
    if (window->Flags & ImGuiWindowFlags_ChildWindow)
6823
0
        resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);
6824
0
    else
6825
0
        resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;
6826
0
    for (int border_n = 0; border_n < 4; border_n++)
6827
0
    {
6828
0
        if ((resize_border_mask & (1 << border_n)) == 0)
6829
0
            continue;
6830
0
        const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6831
0
        const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;
6832
6833
0
        bool hovered, held;
6834
0
        ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, g.WindowsBorderHoverPadding);
6835
0
        ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()
6836
0
        ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);
6837
0
        ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);
6838
        //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));
6839
0
        if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)
6840
0
            hovered = false;
6841
0
        if (hovered || held)
6842
0
            SetMouseCursor((axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS);
6843
0
        if (held && g.IO.MouseDoubleClicked[0])
6844
0
        {
6845
            // Double-clicking bottom or right border auto-fit on this axis
6846
            // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one side may be auto-fit when calculating scrollbars.
6847
            // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.
6848
0
            if (border_n == 1 || border_n == 3) // Right and bottom border
6849
0
            {
6850
0
                size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];
6851
0
                ret_auto_fit_mask |= (1 << axis);
6852
0
                hovered = held = false; // So border doesn't show highlighted at new position
6853
0
            }
6854
0
            ClearActiveID();
6855
0
        }
6856
0
        else if (held)
6857
0
        {
6858
            // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.
6859
            // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.
6860
            // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!
6861
0
            const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false));
6862
0
            if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)
6863
0
            {
6864
0
                g.WindowResizeBorderExpectedRect = border_rect;
6865
0
                g.WindowResizeRelativeMode = false;
6866
0
            }
6867
0
            if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)
6868
0
                g.WindowResizeRelativeMode = true;
6869
6870
0
            const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);
6871
0
            const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];
6872
0
            const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + g.WindowsBorderHoverPadding; // Match ButtonBehavior() padding above.
6873
6874
            // Use absolute mode position
6875
0
            ImVec2 border_target = window->Pos;
6876
0
            border_target[axis] = border_target_abs_mode_for_axis;
6877
6878
            // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.
6879
0
            bool ignore_resize = false;
6880
0
            if (g.WindowResizeRelativeMode)
6881
0
            {
6882
                //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode");
6883
0
                border_target[axis] = border_target_rel_mode_for_axis;
6884
0
                if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))
6885
0
                    ignore_resize = true;
6886
0
            }
6887
6888
            // Clamp, apply
6889
0
            ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);
6890
0
            ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);
6891
0
            border_target = ImClamp(border_target, clamp_min, clamp_max);
6892
0
            if (!ignore_resize)
6893
0
                CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);
6894
0
        }
6895
0
        if (hovered)
6896
0
            *border_hovered = border_n;
6897
0
        if (held)
6898
0
            *border_held = border_n;
6899
0
    }
6900
0
    PopID();
6901
6902
    // Restore nav layer
6903
0
    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
6904
6905
    // Navigation resize (keyboard/gamepad)
6906
    // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.
6907
    // Not even sure the callback works here.
6908
0
    if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)
6909
0
    {
6910
0
        ImVec2 nav_resize_dir;
6911
0
        if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)
6912
0
            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
6913
0
        if (g.NavInputSource == ImGuiInputSource_Gamepad)
6914
0
            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);
6915
0
        if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)
6916
0
        {
6917
0
            const float NAV_RESIZE_SPEED = 600.0f;
6918
0
            const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);
6919
0
            g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;
6920
0
            g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size
6921
0
            g.NavWindowingToggleLayer = false;
6922
0
            g.NavHighlightItemUnderNav = true;
6923
0
            resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);
6924
0
            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);
6925
0
            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
6926
0
            {
6927
                // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.
6928
0
                size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);
6929
0
                g.NavWindowingAccumDeltaSize -= accum_floored;
6930
0
            }
6931
0
        }
6932
0
    }
6933
6934
    // Apply back modified position/size to window
6935
0
    const ImVec2 curr_pos = window->Pos;
6936
0
    const ImVec2 curr_size = window->SizeFull;
6937
0
    if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x))
6938
0
        window->Size.x = window->SizeFull.x = size_target.x;
6939
0
    if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y))
6940
0
        window->Size.y = window->SizeFull.y = size_target.y;
6941
0
    if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x))
6942
0
        window->Pos.x = ImTrunc(pos_target.x);
6943
0
    if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y))
6944
0
        window->Pos.y = ImTrunc(pos_target.y);
6945
0
    if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y)
6946
0
        MarkIniSettingsDirty(window);
6947
6948
    // Recalculate next expected border expected coordinates
6949
0
    if (*border_held != -1)
6950
0
        g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, g.WindowsBorderHoverPadding);
6951
6952
0
    return ret_auto_fit_mask;
6953
80.5k
}
6954
6955
static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)
6956
80.5k
{
6957
80.5k
    ImGuiContext& g = *GImGui;
6958
80.5k
    ImVec2 size_for_clamping = window->Size;
6959
80.5k
    if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6960
0
        size_for_clamping.y = window->TitleBarHeight;
6961
80.5k
    window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);
6962
80.5k
}
6963
6964
static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size)
6965
0
{
6966
0
    const ImGuiResizeBorderDef& def = resize_border_def[border_n];
6967
0
    const float rounding = window->WindowRounding;
6968
0
    const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);
6969
0
    window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);
6970
0
    window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);
6971
0
    window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size);
6972
0
}
6973
6974
static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)
6975
161k
{
6976
161k
    ImGuiContext& g = *GImGui;
6977
161k
    const float border_size = window->WindowBorderSize;
6978
161k
    const ImU32 border_col = GetColorU32(ImGuiCol_Border);
6979
161k
    if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0)
6980
161k
        window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize);
6981
0
    else if (border_size > 0.0f)
6982
0
    {
6983
0
        if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border.
6984
0
            RenderWindowOuterSingleBorder(window, 1, border_col, border_size);
6985
0
        if (window->ChildFlags & ImGuiChildFlags_ResizeY)
6986
0
            RenderWindowOuterSingleBorder(window, 3, border_col, border_size);
6987
0
    }
6988
161k
    if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)
6989
0
    {
6990
0
        const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;
6991
0
        const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
6992
0
        RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual
6993
0
    }
6994
161k
    if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))
6995
0
    {
6996
0
        float y = window->Pos.y + window->TitleBarHeight - 1;
6997
0
        window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize);
6998
0
    }
6999
161k
}
7000
7001
// Draw background and borders
7002
// Draw and handle scrollbars
7003
void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)
7004
161k
{
7005
161k
    ImGuiContext& g = *GImGui;
7006
161k
    ImGuiStyle& style = g.Style;
7007
161k
    ImGuiWindowFlags flags = window->Flags;
7008
7009
    // Ensure that Scrollbar() doesn't read last frame's SkipItems
7010
161k
    IM_ASSERT(window->BeginCount == 0);
7011
161k
    window->SkipItems = false;
7012
161k
    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7013
7014
    // Draw window + handle manual resize
7015
    // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.
7016
161k
    const float window_rounding = window->WindowRounding;
7017
161k
    const float window_border_size = window->WindowBorderSize;
7018
161k
    if (window->Collapsed)
7019
0
    {
7020
        // Title bar only
7021
0
        const float backup_border_size = style.FrameBorderSize;
7022
0
        g.Style.FrameBorderSize = window->WindowBorderSize;
7023
0
        ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);
7024
0
        RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);
7025
0
        g.Style.FrameBorderSize = backup_border_size;
7026
0
    }
7027
161k
    else
7028
161k
    {
7029
        // Window background
7030
161k
        if (!(flags & ImGuiWindowFlags_NoBackground))
7031
161k
        {
7032
161k
            ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));
7033
161k
            bool override_alpha = false;
7034
161k
            float alpha = 1.0f;
7035
161k
            if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasBgAlpha)
7036
0
            {
7037
0
                alpha = g.NextWindowData.BgAlphaVal;
7038
0
                override_alpha = true;
7039
0
            }
7040
161k
            if (override_alpha)
7041
0
                bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);
7042
161k
            window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);
7043
161k
        }
7044
7045
        // Title bar
7046
161k
        if (!(flags & ImGuiWindowFlags_NoTitleBar))
7047
80.5k
        {
7048
80.5k
            ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
7049
80.5k
            window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);
7050
80.5k
        }
7051
7052
        // Menu bar
7053
161k
        if (flags & ImGuiWindowFlags_MenuBar)
7054
80.5k
        {
7055
80.5k
            ImRect menu_bar_rect = window->MenuBarRect();
7056
80.5k
            menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.
7057
80.5k
            window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);
7058
80.5k
            if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)
7059
0
                window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
7060
80.5k
        }
7061
7062
        // Scrollbars
7063
161k
        if (window->ScrollbarX)
7064
0
            Scrollbar(ImGuiAxis_X);
7065
161k
        if (window->ScrollbarY)
7066
0
            Scrollbar(ImGuiAxis_Y);
7067
7068
        // Render resize grips (after their input handling so we don't have a frame of latency)
7069
161k
        if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))
7070
81.2k
        {
7071
243k
            for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)
7072
162k
            {
7073
162k
                const ImU32 col = resize_grip_col[resize_grip_n];
7074
162k
                if ((col & IM_COL32_A_MASK) == 0)
7075
162k
                    continue;
7076
0
                const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];
7077
0
                const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);
7078
0
                const float border_inner = IM_ROUND(window_border_size * 0.5f);
7079
0
                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner)));
7080
0
                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size)));
7081
0
                window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12);
7082
0
                window->DrawList->PathFillConvex(col);
7083
0
            }
7084
81.2k
        }
7085
7086
        // Borders
7087
161k
        if (handle_borders_and_resize_grips)
7088
161k
            RenderWindowOuterBorders(window);
7089
161k
    }
7090
161k
    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7091
161k
}
7092
7093
// Render title text, collapse button, close button
7094
void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)
7095
80.5k
{
7096
80.5k
    ImGuiContext& g = *GImGui;
7097
80.5k
    ImGuiStyle& style = g.Style;
7098
80.5k
    ImGuiWindowFlags flags = window->Flags;
7099
7100
80.5k
    const bool has_close_button = (p_open != NULL);
7101
80.5k
    const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);
7102
7103
    // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)
7104
    // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?
7105
80.5k
    const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;
7106
80.5k
    g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;
7107
80.5k
    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7108
7109
    // Layout buttons
7110
    // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.
7111
80.5k
    float pad_l = style.FramePadding.x;
7112
80.5k
    float pad_r = style.FramePadding.x;
7113
80.5k
    float button_sz = g.FontSize;
7114
80.5k
    ImVec2 close_button_pos;
7115
80.5k
    ImVec2 collapse_button_pos;
7116
80.5k
    if (has_close_button)
7117
0
    {
7118
0
        close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7119
0
        pad_r += button_sz + style.ItemInnerSpacing.x;
7120
0
    }
7121
80.5k
    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)
7122
0
    {
7123
0
        collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);
7124
0
        pad_r += button_sz + style.ItemInnerSpacing.x;
7125
0
    }
7126
80.5k
    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)
7127
80.5k
    {
7128
80.5k
        collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);
7129
80.5k
        pad_l += button_sz + style.ItemInnerSpacing.x;
7130
80.5k
    }
7131
7132
    // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)
7133
80.5k
    if (has_collapse_button)
7134
80.5k
        if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos))
7135
0
            window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function
7136
7137
    // Close button
7138
80.5k
    if (has_close_button)
7139
0
    {
7140
0
        ImGuiItemFlags backup_item_flags = g.CurrentItemFlags;
7141
0
        g.CurrentItemFlags |= ImGuiItemFlags_NoFocus;
7142
0
        if (CloseButton(window->GetID("#CLOSE"), close_button_pos))
7143
0
            *p_open = false;
7144
0
        g.CurrentItemFlags = backup_item_flags;
7145
0
    }
7146
7147
80.5k
    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7148
80.5k
    g.CurrentItemFlags = item_flags_backup;
7149
7150
    // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional "unsaved document" marker)
7151
    // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..
7152
80.5k
    const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;
7153
80.5k
    const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);
7154
7155
    // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,
7156
    // while uncentered title text will still reach edges correctly.
7157
80.5k
    if (pad_l > style.FramePadding.x)
7158
80.5k
        pad_l += g.Style.ItemInnerSpacing.x;
7159
80.5k
    if (pad_r > style.FramePadding.x)
7160
0
        pad_r += g.Style.ItemInnerSpacing.x;
7161
80.5k
    if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)
7162
0
    {
7163
0
        float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center
7164
0
        float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);
7165
0
        pad_l = ImMax(pad_l, pad_extend * centerness);
7166
0
        pad_r = ImMax(pad_r, pad_extend * centerness);
7167
0
    }
7168
7169
80.5k
    ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);
7170
80.5k
    ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);
7171
80.5k
    if (flags & ImGuiWindowFlags_UnsavedDocument)
7172
0
    {
7173
0
        ImVec2 marker_pos;
7174
0
        marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);
7175
0
        marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;
7176
0
        if (marker_pos.x > layout_r.Min.x)
7177
0
        {
7178
0
            RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_UnsavedMarker));
7179
0
            clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));
7180
0
        }
7181
0
    }
7182
    //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7183
    //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]
7184
80.5k
    RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);
7185
80.5k
}
7186
7187
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
7188
161k
{
7189
161k
    window->ParentWindow = parent_window;
7190
161k
    window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
7191
161k
    if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
7192
0
        window->RootWindow = parent_window->RootWindow;
7193
161k
    if (parent_window && (flags & ImGuiWindowFlags_Popup))
7194
660
        window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
7195
161k
    if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)))
7196
660
        window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
7197
161k
    while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened)
7198
0
    {
7199
0
        IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);
7200
0
        window->RootWindowForNav = window->RootWindowForNav->ParentWindow;
7201
0
    }
7202
161k
}
7203
7204
// [EXPERIMENTAL] Called by Begin(). NextWindowData is valid at this point.
7205
// This is designed as a toy/test-bed for
7206
void ImGui::UpdateWindowSkipRefresh(ImGuiWindow* window)
7207
161k
{
7208
161k
    ImGuiContext& g = *GImGui;
7209
161k
    window->SkipRefresh = false;
7210
161k
    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasRefreshPolicy) == 0)
7211
161k
        return;
7212
0
    if (g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_TryToAvoidRefresh)
7213
0
    {
7214
        // FIXME-IDLE: Tests for e.g. mouse clicks or keyboard while focused.
7215
0
        if (window->Appearing) // If currently appearing
7216
0
            return;
7217
0
        if (window->Hidden) // If was hidden (previous frame)
7218
0
            return;
7219
0
        if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnHover) && g.HoveredWindow)
7220
0
            if (window->RootWindow == g.HoveredWindow->RootWindow || IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, window))
7221
0
                return;
7222
0
        if ((g.NextWindowData.RefreshFlagsVal & ImGuiWindowRefreshFlags_RefreshOnFocus) && g.NavWindow)
7223
0
            if (window->RootWindow == g.NavWindow->RootWindow || IsWindowWithinBeginStackOf(g.NavWindow->RootWindow, window))
7224
0
                return;
7225
0
        window->DrawList = NULL;
7226
0
        window->SkipRefresh = true;
7227
0
    }
7228
0
}
7229
7230
static void SetWindowActiveForSkipRefresh(ImGuiWindow* window)
7231
0
{
7232
0
    window->Active = true;
7233
0
    for (ImGuiWindow* child : window->DC.ChildWindows)
7234
0
        if (!child->Hidden)
7235
0
        {
7236
0
            child->Active = child->SkipRefresh = true;
7237
0
            SetWindowActiveForSkipRefresh(child);
7238
0
        }
7239
0
}
7240
7241
// Push a new Dear ImGui window to add widgets to.
7242
// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.
7243
// - Begin/End can be called multiple times during the frame with the same window name to append content.
7244
// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).
7245
//   You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.
7246
// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.
7247
// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.
7248
bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
7249
161k
{
7250
161k
    ImGuiContext& g = *GImGui;
7251
161k
    const ImGuiStyle& style = g.Style;
7252
161k
    IM_ASSERT(name != NULL && name[0] != '\0');     // Window name required
7253
161k
    IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()
7254
161k
    IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet
7255
7256
    // Find or create
7257
161k
    ImGuiWindow* window = FindWindowByName(name);
7258
161k
    const bool window_just_created = (window == NULL);
7259
161k
    if (window_just_created)
7260
3
        window = CreateNewWindow(name, flags);
7261
7262
    // [DEBUG] Debug break requested by user
7263
161k
    if (g.DebugBreakInWindow == window->ID)
7264
0
        IM_DEBUG_BREAK();
7265
7266
    // Automatically disable manual moving/resizing when NoInputs is set
7267
161k
    if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
7268
0
        flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
7269
7270
161k
    const int current_frame = g.FrameCount;
7271
161k
    const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);
7272
161k
    window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);
7273
7274
    // Update the Appearing flag
7275
161k
    bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit "Debug" window would always toggle off->on
7276
161k
    if (flags & ImGuiWindowFlags_Popup)
7277
660
    {
7278
660
        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7279
660
        window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed
7280
660
        window_just_activated_by_user |= (window != popup_ref.Window);
7281
660
    }
7282
161k
    window->Appearing = window_just_activated_by_user;
7283
161k
    if (window->Appearing)
7284
3
        SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
7285
7286
    // Update Flags, LastFrameActive, BeginOrderXXX fields
7287
161k
    if (first_begin_of_the_frame)
7288
161k
    {
7289
161k
        UpdateWindowInFocusOrderList(window, window_just_created, flags);
7290
161k
        window->Flags = (ImGuiWindowFlags)flags;
7291
161k
        window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;
7292
161k
        window->LastFrameActive = current_frame;
7293
161k
        window->LastTimeActive = (float)g.Time;
7294
161k
        window->BeginOrderWithinParent = 0;
7295
161k
        window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);
7296
161k
    }
7297
0
    else
7298
0
    {
7299
0
        flags = window->Flags;
7300
0
    }
7301
7302
    // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
7303
161k
    ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;
7304
161k
    ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) ? parent_window_in_stack : NULL) : window->ParentWindow;
7305
161k
    IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
7306
7307
    // We allow window memory to be compacted so recreate the base stack when needed.
7308
161k
    if (window->IDStack.Size == 0)
7309
0
        window->IDStack.push_back(window->ID);
7310
7311
    // Add to stack
7312
161k
    g.CurrentWindow = window;
7313
161k
    g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1);
7314
161k
    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
7315
161k
    window_stack_data.Window = window;
7316
161k
    window_stack_data.ParentLastItemDataBackup = g.LastItemData;
7317
161k
    window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled);
7318
161k
    window_stack_data.DisabledOverrideReenableAlphaBackup = 0.0f;
7319
161k
    ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin);
7320
161k
    g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin;
7321
161k
    if (flags & ImGuiWindowFlags_ChildMenu)
7322
660
        g.BeginMenuDepth++;
7323
7324
    // Update ->RootWindow and others pointers (before any possible call to FocusWindow)
7325
161k
    if (first_begin_of_the_frame)
7326
161k
    {
7327
161k
        UpdateWindowParentAndRootLinks(window, flags, parent_window);
7328
161k
        window->ParentWindowInBeginStack = parent_window_in_stack;
7329
7330
        // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack,
7331
        // e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798)
7332
161k
        window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL;
7333
7334
        // Inherent SetWindowFontScale() from parent until we fix this system...
7335
161k
        window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f;
7336
161k
    }
7337
7338
    // Add to focus scope stack
7339
161k
    PushFocusScope((window->ChildFlags & ImGuiChildFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID);
7340
161k
    window->NavRootFocusScopeId = g.CurrentFocusScopeId;
7341
7342
    // Add to popup stacks: update OpenPopupStack[] data, push to BeginPopupStack[]
7343
161k
    if (flags & ImGuiWindowFlags_Popup)
7344
660
    {
7345
660
        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];
7346
660
        popup_ref.Window = window;
7347
660
        popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;
7348
660
        g.BeginPopupStack.push_back(popup_ref);
7349
660
        window->PopupId = popup_ref.PopupId;
7350
660
    }
7351
7352
    // Process SetNextWindow***() calls
7353
    // (FIXME: Consider splitting the HasXXX flags into X/Y components)
7354
161k
    bool window_pos_set_by_api = false;
7355
161k
    bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;
7356
161k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos)
7357
81.2k
    {
7358
81.2k
        window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;
7359
81.2k
        if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)
7360
0
        {
7361
            // May be processed on the next frame if this is our first frame and we are measuring size
7362
            // FIXME: Look into removing the branch so everything can go through this same code path for consistency.
7363
0
            window->SetWindowPosVal = g.NextWindowData.PosVal;
7364
0
            window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;
7365
0
            window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
7366
0
        }
7367
81.2k
        else
7368
81.2k
        {
7369
81.2k
            SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);
7370
81.2k
        }
7371
81.2k
    }
7372
161k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize)
7373
161k
    {
7374
161k
        window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);
7375
161k
        window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);
7376
161k
        if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()
7377
0
            g.NextWindowData.SizeVal.x = window->SizeFull.x;
7378
161k
        if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)
7379
0
            g.NextWindowData.SizeVal.y = window->SizeFull.y;
7380
161k
        SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);
7381
161k
    }
7382
161k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll)
7383
0
    {
7384
0
        if (g.NextWindowData.ScrollVal.x >= 0.0f)
7385
0
        {
7386
0
            window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;
7387
0
            window->ScrollTargetCenterRatio.x = 0.0f;
7388
0
        }
7389
0
        if (g.NextWindowData.ScrollVal.y >= 0.0f)
7390
0
        {
7391
0
            window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;
7392
0
            window->ScrollTargetCenterRatio.y = 0.0f;
7393
0
        }
7394
0
    }
7395
161k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasContentSize)
7396
0
        window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;
7397
161k
    else if (first_begin_of_the_frame)
7398
161k
        window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);
7399
161k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasCollapsed)
7400
0
        SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);
7401
161k
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasFocus)
7402
0
        FocusWindow(window);
7403
161k
    if (window->Appearing)
7404
3
        SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);
7405
7406
    // [EXPERIMENTAL] Skip Refresh mode
7407
161k
    UpdateWindowSkipRefresh(window);
7408
7409
    // Nested root windows (typically tooltips) override disabled state
7410
161k
    if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
7411
0
        BeginDisabledOverrideReenable();
7412
7413
    // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()
7414
161k
    g.CurrentWindow = NULL;
7415
7416
    // When reusing window again multiple times a frame, just append content (don't need to setup again)
7417
161k
    if (first_begin_of_the_frame && !window->SkipRefresh)
7418
161k
    {
7419
        // Initialize
7420
161k
        const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)
7421
161k
        const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);
7422
161k
        window->Active = true;
7423
161k
        window->HasCloseButton = (p_open != NULL);
7424
161k
        window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);
7425
161k
        window->IDStack.resize(1);
7426
161k
        window->DrawList->_ResetForNewFrame();
7427
161k
        window->DC.CurrentTableIdx = -1;
7428
7429
        // Restore buffer capacity when woken from a compacted state, to avoid
7430
161k
        if (window->MemoryCompacted)
7431
0
            GcAwakeTransientWindowBuffers(window);
7432
7433
        // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged).
7434
        // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.
7435
161k
        bool window_title_visible_elsewhere = false;
7436
161k
        if (g.NavWindowingListWindow != NULL && (flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB
7437
0
            window_title_visible_elsewhere = true;
7438
161k
        if (flags & ImGuiWindowFlags_ChildMenu)
7439
660
            window_title_visible_elsewhere = true;
7440
161k
        if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)
7441
0
        {
7442
0
            size_t buf_len = (size_t)window->NameBufLen;
7443
0
            window->Name = ImStrdupcpy(window->Name, &buf_len, name);
7444
0
            window->NameBufLen = (int)buf_len;
7445
0
        }
7446
7447
        // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS
7448
7449
        // Update contents size from last frame for auto-fitting (or use explicit size)
7450
161k
        CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);
7451
161k
        if (window->HiddenFramesCanSkipItems > 0)
7452
0
            window->HiddenFramesCanSkipItems--;
7453
161k
        if (window->HiddenFramesCannotSkipItems > 0)
7454
2
            window->HiddenFramesCannotSkipItems--;
7455
161k
        if (window->HiddenFramesForRenderOnly > 0)
7456
0
            window->HiddenFramesForRenderOnly--;
7457
7458
        // Hide new windows for one frame until they calculate their size
7459
161k
        if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))
7460
2
            window->HiddenFramesCannotSkipItems = 1;
7461
7462
        // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)
7463
        // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.
7464
161k
        if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)
7465
1
        {
7466
1
            window->HiddenFramesCannotSkipItems = 1;
7467
1
            if (flags & ImGuiWindowFlags_AlwaysAutoResize)
7468
1
            {
7469
1
                if (!window_size_x_set_by_api)
7470
1
                    window->Size.x = window->SizeFull.x = 0.f;
7471
1
                if (!window_size_y_set_by_api)
7472
1
                    window->Size.y = window->SizeFull.y = 0.f;
7473
1
                window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);
7474
1
            }
7475
1
        }
7476
7477
        // SELECT VIEWPORT
7478
        // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)
7479
7480
161k
        ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
7481
161k
        SetWindowViewport(window, viewport);
7482
161k
        SetCurrentWindow(window);
7483
7484
        // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)
7485
7486
161k
        if (flags & ImGuiWindowFlags_ChildWindow)
7487
0
            window->WindowBorderSize = style.ChildBorderSize;
7488
161k
        else
7489
161k
            window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
7490
161k
        window->WindowPadding = style.WindowPadding;
7491
161k
        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)
7492
0
            window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
7493
7494
        // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.
7495
161k
        window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
7496
161k
        window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
7497
161k
        window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f;
7498
161k
        window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f;
7499
161k
        window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window.
7500
7501
        // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible.
7502
        // Those flags will be altered further down in the function depending on more conditions.
7503
161k
        bool use_current_size_for_scrollbar_x = window_just_created;
7504
161k
        bool use_current_size_for_scrollbar_y = window_just_created;
7505
161k
        if (window_size_x_set_by_api && window->ContentSizeExplicit.x != 0.0f)
7506
0
            use_current_size_for_scrollbar_x = true;
7507
161k
        if (window_size_y_set_by_api && window->ContentSizeExplicit.y != 0.0f) // #7252
7508
0
            use_current_size_for_scrollbar_y = true;
7509
7510
        // Collapse window by double-clicking on title bar
7511
        // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
7512
161k
        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
7513
80.5k
        {
7514
            // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed),
7515
            // so verify that we don't have items over the title bar.
7516
80.5k
            ImRect title_bar_rect = window->TitleBarRect();
7517
80.5k
            if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && g.ActiveId == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max))
7518
0
                if (g.IO.MouseClickedCount[0] == 2 && GetKeyOwner(ImGuiKey_MouseLeft) == ImGuiKeyOwner_NoOwner)
7519
0
                    window->WantCollapseToggle = true;
7520
80.5k
            if (window->WantCollapseToggle)
7521
0
            {
7522
0
                window->Collapsed = !window->Collapsed;
7523
0
                if (!window->Collapsed)
7524
0
                    use_current_size_for_scrollbar_y = true;
7525
0
                MarkIniSettingsDirty(window);
7526
0
            }
7527
80.5k
        }
7528
81.2k
        else
7529
81.2k
        {
7530
81.2k
            window->Collapsed = false;
7531
81.2k
        }
7532
161k
        window->WantCollapseToggle = false;
7533
7534
        // SIZE
7535
7536
        // Outer Decoration Sizes
7537
        // (we need to clear ScrollbarSize immediately as CalcWindowAutoFitSize() needs it and can be called from other locations).
7538
161k
        const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;
7539
161k
        window->DecoOuterSizeX1 = 0.0f;
7540
161k
        window->DecoOuterSizeX2 = 0.0f;
7541
161k
        window->DecoOuterSizeY1 = window->TitleBarHeight + window->MenuBarHeight;
7542
161k
        window->DecoOuterSizeY2 = 0.0f;
7543
161k
        window->ScrollbarSizes = ImVec2(0.0f, 0.0f);
7544
7545
        // Calculate auto-fit size, handle automatic resize
7546
161k
        const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);
7547
161k
        if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)
7548
660
        {
7549
            // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.
7550
660
            if (!window_size_x_set_by_api)
7551
660
            {
7552
660
                window->SizeFull.x = size_auto_fit.x;
7553
660
                use_current_size_for_scrollbar_x = true;
7554
660
            }
7555
660
            if (!window_size_y_set_by_api)
7556
660
            {
7557
660
                window->SizeFull.y = size_auto_fit.y;
7558
660
                use_current_size_for_scrollbar_y = true;
7559
660
            }
7560
660
        }
7561
161k
        else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7562
0
        {
7563
            // Auto-fit may only grow window during the first few frames
7564
            // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.
7565
0
            if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)
7566
0
            {
7567
0
                window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;
7568
0
                use_current_size_for_scrollbar_x = true;
7569
0
            }
7570
0
            if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)
7571
0
            {
7572
0
                window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;
7573
0
                use_current_size_for_scrollbar_y = true;
7574
0
            }
7575
0
            if (!window->Collapsed)
7576
0
                MarkIniSettingsDirty(window);
7577
0
        }
7578
7579
        // Apply minimum/maximum window size constraints and final size
7580
161k
        window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);
7581
161k
        window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
7582
7583
        // POSITION
7584
7585
        // Popup latch its initial position, will position itself when it appears next frame
7586
161k
        if (window_just_activated_by_user)
7587
3
        {
7588
3
            window->AutoPosLastDirection = ImGuiDir_None;
7589
3
            if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()
7590
0
                window->Pos = g.BeginPopupStack.back().OpenPopupPos;
7591
3
        }
7592
7593
        // Position child window
7594
161k
        if (flags & ImGuiWindowFlags_ChildWindow)
7595
0
        {
7596
0
            IM_ASSERT(parent_window && parent_window->Active);
7597
0
            window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;
7598
0
            parent_window->DC.ChildWindows.push_back(window);
7599
0
            if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)
7600
0
                window->Pos = parent_window->DC.CursorPos;
7601
0
        }
7602
7603
161k
        const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);
7604
161k
        if (window_pos_with_pivot)
7605
0
            SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)
7606
161k
        else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)
7607
660
            window->Pos = FindBestWindowPosForPopup(window);
7608
161k
        else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)
7609
0
            window->Pos = FindBestWindowPosForPopup(window);
7610
161k
        else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)
7611
0
            window->Pos = FindBestWindowPosForPopup(window);
7612
7613
        // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)
7614
        // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.
7615
161k
        ImRect viewport_rect(viewport->GetMainRect());
7616
161k
        ImRect viewport_work_rect(viewport->GetWorkRect());
7617
161k
        ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
7618
161k
        ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);
7619
7620
        // Clamp position/size so window stays visible within its viewport or monitor
7621
        // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
7622
161k
        if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))
7623
80.5k
            if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)
7624
80.5k
                ClampWindowPos(window, visibility_rect);
7625
161k
        window->Pos = ImTrunc(window->Pos);
7626
7627
        // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)
7628
        // Large values tend to lead to variety of artifacts and are not recommended.
7629
161k
        window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;
7630
7631
        // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.
7632
        //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))
7633
        //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);
7634
7635
        // Apply window focus (new and reactivated windows are moved to front)
7636
161k
        bool want_focus = false;
7637
161k
        if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
7638
3
        {
7639
3
            if (flags & ImGuiWindowFlags_Popup)
7640
1
                want_focus = true;
7641
2
            else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)
7642
2
                want_focus = true;
7643
3
        }
7644
7645
        // [Test Engine] Register whole window in the item system (before submitting further decorations)
7646
#ifdef IMGUI_ENABLE_TEST_ENGINE
7647
        if (g.TestEngineHookItems)
7648
        {
7649
            IM_ASSERT(window->IDStack.Size == 1);
7650
            window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.
7651
            window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7652
            IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);
7653
            IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);
7654
            window->IDStack.Size = 1;
7655
            window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7656
7657
        }
7658
#endif
7659
7660
        // Decide if we are going to handle borders and resize grips
7661
        // 'window->SkipItems' is not updated yet so for child windows we rely on ParentWindow to avoid submitting decorations. (#8815)
7662
        // Whenever we add support for full decorated child windows we will likely make this logic more general.
7663
161k
        bool handle_borders_and_resize_grips = true;
7664
161k
        if ((flags & ImGuiWindowFlags_ChildWindow) && window->ParentWindow->SkipItems)
7665
0
            handle_borders_and_resize_grips = false;
7666
7667
        // Handle manual resize: Resize Grips, Borders, Gamepad
7668
        // Child windows can only be resized when they have the flags set. The resize grip allows resizing in both directions, so it should appear only if both flags are set.
7669
161k
        int border_hovered = -1, border_held = -1;
7670
161k
        ImU32 resize_grip_col[4] = {};
7671
161k
        int resize_grip_count;
7672
161k
        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup))
7673
0
            resize_grip_count = ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->ChildFlags & ImGuiChildFlags_ResizeY)) ? 1 : 0;
7674
161k
        else
7675
161k
            resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.
7676
7677
161k
        const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));
7678
161k
        if (handle_borders_and_resize_grips && !window->Collapsed)
7679
161k
            if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))
7680
0
            {
7681
0
                if (auto_fit_mask & (1 << ImGuiAxis_X))
7682
0
                    use_current_size_for_scrollbar_x = true;
7683
0
                if (auto_fit_mask & (1 << ImGuiAxis_Y))
7684
0
                    use_current_size_for_scrollbar_y = true;
7685
0
            }
7686
161k
        window->ResizeBorderHovered = (signed char)border_hovered;
7687
161k
        window->ResizeBorderHeld = (signed char)border_held;
7688
7689
        // SCROLLBAR VISIBILITY
7690
7691
        // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).
7692
161k
        if (!window->Collapsed)
7693
161k
        {
7694
            // When reading the current size we need to read it after size constraints have been applied.
7695
            // Intentionally use previous frame values for InnerRect and ScrollbarSizes.
7696
            // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.
7697
161k
            ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));
7698
161k
            ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;
7699
161k
            ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;
7700
161k
            float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;
7701
161k
            float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;
7702
161k
            bool scrollbar_x_prev = window->ScrollbarX;
7703
            //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?
7704
161k
            window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));
7705
161k
            window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));
7706
7707
            // Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488)
7708
            // (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause.
7709
            //  The better solution is to, either (1) enforce visibility by using ImGuiWindowFlags_AlwaysHorizontalScrollbar or (2) declare stable contents width with SetNextWindowContentSize(), if you can compute it)
7710
161k
            window->ScrollbarXStabilizeToggledHistory <<= 1;
7711
161k
            window->ScrollbarXStabilizeToggledHistory |= (scrollbar_x_prev != window->ScrollbarX) ? 0x01 : 0x00;
7712
161k
            const bool scrollbar_x_stabilize = (window->ScrollbarXStabilizeToggledHistory != 0) && ImCountSetBits(window->ScrollbarXStabilizeToggledHistory) >= 4; // 4 == half of bits in our U8 history.
7713
161k
            if (scrollbar_x_stabilize)
7714
0
                window->ScrollbarX = true;
7715
            //if (scrollbar_x_stabilize && !window->ScrollbarXStabilizeEnabled)
7716
            //    IMGUI_DEBUG_LOG("[scroll] Stabilize ScrollbarX for Window '%s'\n", window->Name);
7717
161k
            window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize;
7718
7719
161k
            if (window->ScrollbarX && !window->ScrollbarY)
7720
0
                window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar);
7721
161k
            window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);
7722
7723
            // Amend the partially filled window->DecorationXXX values.
7724
161k
            window->DecoOuterSizeX2 += window->ScrollbarSizes.x;
7725
161k
            window->DecoOuterSizeY2 += window->ScrollbarSizes.y;
7726
161k
        }
7727
7728
        // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)
7729
        // Update various regions. Variables they depend on should be set above in this function.
7730
        // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.
7731
7732
        // Outer rectangle
7733
        // Not affected by window border size. Used by:
7734
        // - FindHoveredWindow() (w/ extra padding when border resize is enabled)
7735
        // - Begin() initial clipping rect for drawing window background and borders.
7736
        // - Begin() clipping whole child
7737
161k
        const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;
7738
161k
        const ImRect outer_rect = window->Rect();
7739
161k
        const ImRect title_bar_rect = window->TitleBarRect();
7740
161k
        window->OuterRectClipped = outer_rect;
7741
161k
        window->OuterRectClipped.ClipWith(host_rect);
7742
7743
        // Inner rectangle
7744
        // Not affected by window border size. Used by:
7745
        // - InnerClipRect
7746
        // - ScrollToRectEx()
7747
        // - NavUpdatePageUpPageDown()
7748
        // - Scrollbar()
7749
161k
        window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;
7750
161k
        window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;
7751
161k
        window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;
7752
161k
        window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;
7753
7754
        // Inner clipping rectangle.
7755
        // - Extend a outside of normal work region up to borders.
7756
        // - This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.
7757
        // - It also makes clipped items be more noticeable.
7758
        // - And is consistent on both axis (prior to 2024/05/03 ClipRect used WindowPadding.x * 0.5f on left and right edge), see #3312
7759
        // - Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.
7760
        // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.
7761
        // Affected by window/frame border size. Used by:
7762
        // - Begin() initial clip rect
7763
161k
        float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);
7764
7765
        // Try to match the fact that our border is drawn centered over the window rectangle, rather than inner.
7766
        // This is why we do a *0.5f here. We don't currently even technically support large values for WindowBorderSize,
7767
        // see e.g #7887 #7888, but may do after we move the window border to become an inner border (and then we can remove the 0.5f here).
7768
161k
        window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + window->WindowBorderSize * 0.5f);
7769
161k
        window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size * 0.5f);
7770
161k
        window->InnerClipRect.Max.x = ImFloor(window->InnerRect.Max.x - window->WindowBorderSize * 0.5f);
7771
161k
        window->InnerClipRect.Max.y = ImFloor(window->InnerRect.Max.y - window->WindowBorderSize * 0.5f);
7772
161k
        window->InnerClipRect.ClipWithFull(host_rect);
7773
7774
        // SCROLLING
7775
7776
        // Lock down maximum scrolling
7777
        // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate
7778
        // for right/bottom aligned items without creating a scrollbar.
7779
161k
        window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());
7780
161k
        window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());
7781
7782
        // Apply scrolling
7783
161k
        window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);
7784
161k
        window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
7785
161k
        window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;
7786
7787
        // DRAWING
7788
7789
        // Setup draw list and outer clipping rectangle
7790
161k
        IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);
7791
161k
        window->DrawList->PushTexture(g.Font->ContainerAtlas->TexRef);
7792
161k
        PushClipRect(host_rect.Min, host_rect.Max, false);
7793
7794
        // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)
7795
        // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.
7796
        // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)
7797
161k
        {
7798
161k
            bool render_decorations_in_parent = false;
7799
161k
            if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)
7800
0
            {
7801
                // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)
7802
                // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs
7803
0
                ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;
7804
0
                bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;
7805
0
                bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);
7806
0
                if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)
7807
0
                    render_decorations_in_parent = true;
7808
0
            }
7809
161k
            if (render_decorations_in_parent)
7810
0
                window->DrawList = parent_window->DrawList;
7811
7812
            // Handle title bar, scrollbar, resize grips and resize borders
7813
161k
            const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
7814
161k
            const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
7815
161k
            RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);
7816
7817
161k
            if (render_decorations_in_parent)
7818
0
                window->DrawList = &window->DrawListInst;
7819
161k
        }
7820
7821
        // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)
7822
7823
        // Work rectangle.
7824
        // Affected by window padding and border size. Used by:
7825
        // - Columns() for right-most edge
7826
        // - TreeNode(), CollapsingHeader() for right-most edge
7827
        // - BeginTabBar() for right-most edge
7828
161k
        const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);
7829
161k
        const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);
7830
161k
        const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
7831
161k
        const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
7832
161k
        window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));
7833
161k
        window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));
7834
161k
        window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;
7835
161k
        window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;
7836
161k
        window->ParentWorkRect = window->WorkRect;
7837
7838
        // [LEGACY] Content Region
7839
        // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.
7840
        // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.
7841
        // Used by:
7842
        // - Mouse wheel scrolling + many other things
7843
161k
        window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;
7844
161k
        window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;
7845
161k
        window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));
7846
161k
        window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));
7847
7848
        // Setup drawing context
7849
        // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)
7850
161k
        window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;
7851
161k
        window->DC.GroupOffset.x = 0.0f;
7852
161k
        window->DC.ColumnsOffset.x = 0.0f;
7853
7854
        // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.
7855
        // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.
7856
161k
        double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;
7857
161k
        double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;
7858
161k
        window->DC.CursorStartPos  = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);
7859
161k
        window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));
7860
161k
        window->DC.CursorPos = window->DC.CursorStartPos;
7861
161k
        window->DC.CursorPosPrevLine = window->DC.CursorPos;
7862
161k
        window->DC.CursorMaxPos = window->DC.CursorStartPos;
7863
161k
        window->DC.IdealMaxPos = window->DC.CursorStartPos;
7864
161k
        window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);
7865
161k
        window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;
7866
161k
        window->DC.IsSameLine = window->DC.IsSetPos = false;
7867
7868
161k
        window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7869
161k
        window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;
7870
161k
        window->DC.NavLayersActiveMaskNext = 0x00;
7871
161k
        window->DC.NavIsScrollPushableX = true;
7872
161k
        window->DC.NavHideHighlightOneFrame = false;
7873
161k
        window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);
7874
7875
161k
        window->DC.MenuBarAppending = false;
7876
161k
        window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);
7877
161k
        window->DC.TreeDepth = 0;
7878
161k
        window->DC.TreeHasStackDataDepthMask = window->DC.TreeRecordsClippedNodesY2Mask = 0x00;
7879
161k
        window->DC.ChildWindows.resize(0);
7880
161k
        window->DC.StateStorage = &window->StateStorage;
7881
161k
        window->DC.CurrentColumns = NULL;
7882
161k
        window->DC.LayoutType = ImGuiLayoutType_Vertical;
7883
161k
        window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;
7884
7885
        // Default item width. Make it proportional to window size if window manually resizes
7886
161k
        if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))
7887
161k
            window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);
7888
660
        else
7889
660
            window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);
7890
161k
        window->DC.ItemWidth = window->ItemWidthDefault;
7891
161k
        window->DC.TextWrapPos = -1.0f; // disabled
7892
161k
        window->DC.ItemWidthStack.resize(0);
7893
161k
        window->DC.TextWrapPosStack.resize(0);
7894
161k
        if (flags & ImGuiWindowFlags_Modal)
7895
0
            window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg));
7896
7897
161k
        if (window->AutoFitFramesX > 0)
7898
2
            window->AutoFitFramesX--;
7899
161k
        if (window->AutoFitFramesY > 0)
7900
2
            window->AutoFitFramesY--;
7901
7902
        // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)
7903
        // We ImGuiFocusRequestFlags_UnlessBelowModal to:
7904
        // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.
7905
        // - Position window behind the modal that is not a begin-parent of this window.
7906
161k
        if (want_focus)
7907
3
            FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
7908
161k
        if (want_focus && window == g.NavWindow)
7909
3
            NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls
7910
7911
        // Pressing CTRL+C copy window content into the clipboard
7912
        // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope.
7913
        // [EXPERIMENTAL] Text outputs has many issues.
7914
161k
        if (g.IO.ConfigWindowsCopyContentsWithCtrlC)
7915
0
            if (g.NavWindow && g.NavWindow->RootWindow == window && g.ActiveId == 0 && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C))
7916
0
                LogToClipboard(0);
7917
7918
        // Title bar
7919
161k
        if (!(flags & ImGuiWindowFlags_NoTitleBar))
7920
80.5k
            RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);
7921
7922
        // Clear hit test shape every frame
7923
161k
        window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
7924
7925
161k
        if (flags & ImGuiWindowFlags_Tooltip)
7926
0
            g.TooltipPreviousWindow = window;
7927
7928
        // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
7929
        // This is useful to allow creating context menus on title bar only, etc.
7930
161k
        window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None;
7931
161k
        window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
7932
161k
        SetLastItemDataForWindow(window, title_bar_rect);
7933
7934
        // [DEBUG]
7935
161k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
7936
161k
        if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))
7937
0
            DebugLocateItemResolveWithLastItem();
7938
161k
#endif
7939
7940
        // [Test Engine] Register title bar / tab with MoveId.
7941
#ifdef IMGUI_ENABLE_TEST_ENGINE
7942
        if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))
7943
        {
7944
            window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
7945
            IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);
7946
            window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
7947
        }
7948
#endif
7949
161k
    }
7950
0
    else
7951
0
    {
7952
        // Skip refresh always mark active
7953
0
        if (window->SkipRefresh)
7954
0
            SetWindowActiveForSkipRefresh(window);
7955
7956
        // Append
7957
0
        SetCurrentWindow(window);
7958
0
        SetLastItemDataForWindow(window, window->TitleBarRect());
7959
0
    }
7960
7961
161k
    if (!window->SkipRefresh)
7962
161k
        PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
7963
7964
    // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
7965
161k
    window->WriteAccessed = false;
7966
161k
    window->BeginCount++;
7967
161k
    g.NextWindowData.ClearFlags();
7968
7969
    // Update visibility
7970
161k
    if (first_begin_of_the_frame && !window->SkipRefresh)
7971
161k
    {
7972
161k
        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))
7973
0
        {
7974
            // Child window can be out of sight and have "negative" clip windows.
7975
            // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
7976
0
            IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
7977
0
            const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);
7978
0
            if (!g.LogEnabled && !nav_request)
7979
0
                if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
7980
0
                {
7981
0
                    if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)
7982
0
                        window->HiddenFramesCannotSkipItems = 1;
7983
0
                    else
7984
0
                        window->HiddenFramesCanSkipItems = 1;
7985
0
                }
7986
7987
            // Hide along with parent or if parent is collapsed
7988
0
            if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))
7989
0
                window->HiddenFramesCanSkipItems = 1;
7990
0
            if (parent_window && parent_window->HiddenFramesCannotSkipItems > 0)
7991
0
                window->HiddenFramesCannotSkipItems = 1;
7992
0
        }
7993
7994
        // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)
7995
161k
        if (style.Alpha <= 0.0f)
7996
0
            window->HiddenFramesCanSkipItems = 1;
7997
7998
        // Update the Hidden flag
7999
161k
        bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);
8000
161k
        window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);
8001
8002
        // Disable inputs for requested number of frames
8003
161k
        if (window->DisableInputsFrames > 0)
8004
0
        {
8005
0
            window->DisableInputsFrames--;
8006
0
            window->Flags |= ImGuiWindowFlags_NoInputs;
8007
0
        }
8008
8009
        // Update the SkipItems flag, used to early out of all items functions (no layout required)
8010
161k
        bool skip_items = false;
8011
161k
        if (window->Collapsed || !window->Active || hidden_regular)
8012
2
            if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)
8013
0
                skip_items = true;
8014
161k
        window->SkipItems = skip_items;
8015
161k
    }
8016
0
    else if (first_begin_of_the_frame)
8017
0
    {
8018
        // Skip refresh mode
8019
0
        window->SkipItems = true;
8020
0
    }
8021
8022
    // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.
8023
    // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)
8024
161k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8025
161k
    if (!window->IsFallbackWindow)
8026
81.2k
        if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))
8027
0
        {
8028
0
            if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }
8029
0
            if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }
8030
0
            return false;
8031
0
        }
8032
161k
#endif
8033
8034
161k
    return !window->SkipItems;
8035
161k
}
8036
8037
void ImGui::End()
8038
161k
{
8039
161k
    ImGuiContext& g = *GImGui;
8040
161k
    ImGuiWindow* window = g.CurrentWindow;
8041
8042
    // Error checking: verify that user hasn't called End() too many times!
8043
161k
    if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)
8044
0
    {
8045
0
        IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, "Calling End() too many times!");
8046
0
        return;
8047
0
    }
8048
161k
    ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back();
8049
8050
    // Error checking: verify that user doesn't directly call End() on a child window.
8051
161k
    if (window->Flags & ImGuiWindowFlags_ChildWindow)
8052
0
        IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!");
8053
8054
    // Close anything that is open
8055
161k
    if (window->DC.CurrentColumns)
8056
0
        EndColumns();
8057
161k
    if (!window->SkipRefresh)
8058
161k
        PopClipRect();   // Inner window clip rectangle
8059
161k
    PopFocusScope();
8060
161k
    if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window)
8061
0
        EndDisabledOverrideReenable();
8062
8063
161k
    if (window->SkipRefresh)
8064
0
    {
8065
0
        IM_ASSERT(window->DrawList == NULL);
8066
0
        window->DrawList = &window->DrawListInst;
8067
0
    }
8068
8069
    // Stop logging
8070
161k
    if (g.LogWindow == window) // FIXME: add more options for scope of logging
8071
0
        LogFinish();
8072
8073
161k
    if (window->DC.IsSetPos)
8074
0
        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
8075
8076
    // Pop from window stack
8077
161k
    g.LastItemData = window_stack_data.ParentLastItemDataBackup;
8078
161k
    if (window->Flags & ImGuiWindowFlags_ChildMenu)
8079
660
        g.BeginMenuDepth--;
8080
161k
    if (window->Flags & ImGuiWindowFlags_Popup)
8081
660
        g.BeginPopupStack.pop_back();
8082
8083
    // Error handling, state recovery
8084
161k
    if (g.IO.ConfigErrorRecovery)
8085
161k
        ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin);
8086
8087
161k
    g.CurrentWindowStack.pop_back();
8088
161k
    SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);
8089
161k
}
8090
8091
void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)
8092
1.97k
{
8093
1.97k
    ImGuiContext& g = *GImGui;
8094
1.97k
    ImGuiItemFlags item_flags = g.CurrentItemFlags;
8095
1.97k
    IM_ASSERT(item_flags == g.ItemFlagsStack.back());
8096
1.97k
    if (enabled)
8097
1.97k
        item_flags |= option;
8098
0
    else
8099
0
        item_flags &= ~option;
8100
1.97k
    g.CurrentItemFlags = item_flags;
8101
1.97k
    g.ItemFlagsStack.push_back(item_flags);
8102
1.97k
}
8103
8104
void ImGui::PopItemFlag()
8105
1.97k
{
8106
1.97k
    ImGuiContext& g = *GImGui;
8107
1.97k
    if (g.ItemFlagsStack.Size <= 1)
8108
0
    {
8109
0
        IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!");
8110
0
        return;
8111
0
    }
8112
1.97k
    g.ItemFlagsStack.pop_back();
8113
1.97k
    g.CurrentItemFlags = g.ItemFlagsStack.back();
8114
1.97k
}
8115
8116
// BeginDisabled()/EndDisabled()
8117
// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
8118
// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.
8119
// - Feedback welcome at https://github.com/ocornut/imgui/issues/211
8120
// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions.
8121
//   (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
8122
// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED.
8123
void ImGui::BeginDisabled(bool disabled)
8124
0
{
8125
0
    ImGuiContext& g = *GImGui;
8126
0
    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8127
0
    if (!was_disabled && disabled)
8128
0
    {
8129
0
        g.DisabledAlphaBackup = g.Style.Alpha;
8130
0
        g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);
8131
0
    }
8132
0
    if (was_disabled || disabled)
8133
0
        g.CurrentItemFlags |= ImGuiItemFlags_Disabled;
8134
0
    g.ItemFlagsStack.push_back(g.CurrentItemFlags); // FIXME-OPT: can we simply skip this and use DisabledStackSize?
8135
0
    g.DisabledStackSize++;
8136
0
}
8137
8138
void ImGui::EndDisabled()
8139
0
{
8140
0
    ImGuiContext& g = *GImGui;
8141
0
    if (g.DisabledStackSize <= 0)
8142
0
    {
8143
0
        IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!");
8144
0
        return;
8145
0
    }
8146
0
    g.DisabledStackSize--;
8147
0
    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
8148
    //PopItemFlag();
8149
0
    g.ItemFlagsStack.pop_back();
8150
0
    g.CurrentItemFlags = g.ItemFlagsStack.back();
8151
0
    if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
8152
0
        g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();
8153
0
}
8154
8155
// Could have been called BeginDisabledDisable() but it didn't want to be award nominated for most awkward function name.
8156
// Ideally we would use a shared e.g. BeginDisabled()->BeginDisabledEx() but earlier needs to be optimal.
8157
// The whole code for this is awkward, will reevaluate if we find a way to implement SetNextItemDisabled().
8158
void ImGui::BeginDisabledOverrideReenable()
8159
0
{
8160
0
    ImGuiContext& g = *GImGui;
8161
0
    IM_ASSERT(g.CurrentItemFlags & ImGuiItemFlags_Disabled);
8162
0
    g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup = g.Style.Alpha;
8163
0
    g.Style.Alpha = g.DisabledAlphaBackup;
8164
0
    g.CurrentItemFlags &= ~ImGuiItemFlags_Disabled;
8165
0
    g.ItemFlagsStack.push_back(g.CurrentItemFlags);
8166
0
    g.DisabledStackSize++;
8167
0
}
8168
8169
void ImGui::EndDisabledOverrideReenable()
8170
0
{
8171
0
    ImGuiContext& g = *GImGui;
8172
0
    g.DisabledStackSize--;
8173
0
    IM_ASSERT(g.DisabledStackSize > 0);
8174
0
    g.ItemFlagsStack.pop_back();
8175
0
    g.CurrentItemFlags = g.ItemFlagsStack.back();
8176
0
    g.Style.Alpha = g.CurrentWindowStack.back().DisabledOverrideReenableAlphaBackup;
8177
0
}
8178
8179
void ImGui::PushTextWrapPos(float wrap_pos_x)
8180
0
{
8181
0
    ImGuiContext& g = *GImGui;
8182
0
    ImGuiWindow* window = g.CurrentWindow;
8183
0
    window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);
8184
0
    window->DC.TextWrapPos = wrap_pos_x;
8185
0
}
8186
8187
void ImGui::PopTextWrapPos()
8188
0
{
8189
0
    ImGuiContext& g = *GImGui;
8190
0
    ImGuiWindow* window = g.CurrentWindow;
8191
0
    if (window->DC.TextWrapPosStack.Size <= 0)
8192
0
    {
8193
0
        IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!");
8194
0
        return;
8195
0
    }
8196
0
    window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();
8197
0
    window->DC.TextWrapPosStack.pop_back();
8198
0
}
8199
8200
static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)
8201
1.97k
{
8202
1.97k
    ImGuiWindow* last_window = NULL;
8203
5.93k
    while (last_window != window)
8204
3.95k
    {
8205
3.95k
        last_window = window;
8206
3.95k
        window = window->RootWindow;
8207
3.95k
        if (popup_hierarchy)
8208
3.95k
            window = window->RootWindowPopupTree;
8209
3.95k
    }
8210
1.97k
    return window;
8211
1.97k
}
8212
8213
bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy)
8214
1.97k
{
8215
1.97k
    ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);
8216
1.97k
    if (window_root == potential_parent)
8217
1.97k
        return true;
8218
0
    while (window != NULL)
8219
0
    {
8220
0
        if (window == potential_parent)
8221
0
            return true;
8222
0
        if (window == window_root) // end of chain
8223
0
            return false;
8224
0
        window = window->ParentWindow;
8225
0
    }
8226
0
    return false;
8227
0
}
8228
8229
bool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)
8230
1
{
8231
1
    if (window->RootWindow == potential_parent)
8232
1
        return true;
8233
0
    while (window != NULL)
8234
0
    {
8235
0
        if (window == potential_parent)
8236
0
            return true;
8237
0
        window = window->ParentWindowInBeginStack;
8238
0
    }
8239
0
    return false;
8240
0
}
8241
8242
bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)
8243
0
{
8244
0
    ImGuiContext& g = *GImGui;
8245
8246
    // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array
8247
0
    const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);
8248
0
    if (display_layer_delta != 0)
8249
0
        return display_layer_delta > 0;
8250
8251
0
    for (int i = g.Windows.Size - 1; i >= 0; i--)
8252
0
    {
8253
0
        ImGuiWindow* candidate_window = g.Windows[i];
8254
0
        if (candidate_window == potential_above)
8255
0
            return true;
8256
0
        if (candidate_window == potential_below)
8257
0
            return false;
8258
0
    }
8259
0
    return false;
8260
0
}
8261
8262
// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.
8263
// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,
8264
// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!
8265
// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
8266
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
8267
0
{
8268
0
    ImGuiContext& g = *GImGui;
8269
0
    IM_ASSERT_USER_ERROR((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0, "Invalid flags for IsWindowHovered()!");
8270
8271
0
    ImGuiWindow* ref_window = g.HoveredWindow;
8272
0
    ImGuiWindow* cur_window = g.CurrentWindow;
8273
0
    if (ref_window == NULL)
8274
0
        return false;
8275
8276
0
    if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
8277
0
    {
8278
0
        IM_ASSERT(cur_window); // Not inside a Begin()/End()
8279
0
        const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
8280
0
        if (flags & ImGuiHoveredFlags_RootWindow)
8281
0
            cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
8282
8283
0
        bool result;
8284
0
        if (flags & ImGuiHoveredFlags_ChildWindows)
8285
0
            result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
8286
0
        else
8287
0
            result = (ref_window == cur_window);
8288
0
        if (!result)
8289
0
            return false;
8290
0
    }
8291
8292
0
    if (!IsWindowContentHoverable(ref_window, flags))
8293
0
        return false;
8294
0
    if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
8295
0
        if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
8296
0
            return false;
8297
8298
    // When changing hovered window we requires a bit of stationary delay before activating hover timer.
8299
    // FIXME: We don't support delay other than stationary one for now, other delay would need a way
8300
    // to fulfill the possibility that multiple IsWindowHovered() with varying flag could return true
8301
    // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
8302
    // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
8303
0
    if (flags & ImGuiHoveredFlags_ForTooltip)
8304
0
        flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);
8305
0
    if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
8306
0
        return false;
8307
8308
0
    return true;
8309
0
}
8310
8311
float ImGui::GetWindowWidth()
8312
0
{
8313
0
    ImGuiWindow* window = GImGui->CurrentWindow;
8314
0
    return window->Size.x;
8315
0
}
8316
8317
float ImGui::GetWindowHeight()
8318
0
{
8319
0
    ImGuiWindow* window = GImGui->CurrentWindow;
8320
0
    return window->Size.y;
8321
0
}
8322
8323
ImVec2 ImGui::GetWindowPos()
8324
0
{
8325
0
    ImGuiContext& g = *GImGui;
8326
0
    ImGuiWindow* window = g.CurrentWindow;
8327
0
    return window->Pos;
8328
0
}
8329
8330
void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
8331
81.2k
{
8332
    // Test condition (NB: bit 0 is always true) and clear flags for next time
8333
81.2k
    if (cond && (window->SetWindowPosAllowFlags & cond) == 0)
8334
0
        return;
8335
8336
81.2k
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8337
81.2k
    window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8338
81.2k
    window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);
8339
8340
    // Set
8341
81.2k
    const ImVec2 old_pos = window->Pos;
8342
81.2k
    window->Pos = ImTrunc(pos);
8343
81.2k
    ImVec2 offset = window->Pos - old_pos;
8344
81.2k
    if (offset.x == 0.0f && offset.y == 0.0f)
8345
80.5k
        return;
8346
661
    MarkIniSettingsDirty(window);
8347
661
    window->DC.CursorPos += offset;         // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor
8348
661
    window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.
8349
661
    window->DC.IdealMaxPos += offset;
8350
661
    window->DC.CursorStartPos += offset;
8351
661
}
8352
8353
void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)
8354
0
{
8355
0
    ImGuiWindow* window = GetCurrentWindowRead();
8356
0
    SetWindowPos(window, pos, cond);
8357
0
}
8358
8359
void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)
8360
0
{
8361
0
    if (ImGuiWindow* window = FindWindowByName(name))
8362
0
        SetWindowPos(window, pos, cond);
8363
0
}
8364
8365
ImVec2 ImGui::GetWindowSize()
8366
0
{
8367
0
    ImGuiWindow* window = GetCurrentWindowRead();
8368
0
    return window->Size;
8369
0
}
8370
8371
void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)
8372
161k
{
8373
    // Test condition (NB: bit 0 is always true) and clear flags for next time
8374
161k
    if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)
8375
80.5k
        return;
8376
8377
80.5k
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8378
80.5k
    window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8379
8380
    // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)
8381
80.5k
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8382
80.5k
        window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;
8383
80.5k
    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)
8384
80.5k
        window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;
8385
8386
    // Set
8387
80.5k
    ImVec2 old_size = window->SizeFull;
8388
80.5k
    if (size.x <= 0.0f)
8389
0
        window->AutoFitOnlyGrows = false;
8390
80.5k
    else
8391
80.5k
        window->SizeFull.x = IM_TRUNC(size.x);
8392
80.5k
    if (size.y <= 0.0f)
8393
0
        window->AutoFitOnlyGrows = false;
8394
80.5k
    else
8395
80.5k
        window->SizeFull.y = IM_TRUNC(size.y);
8396
80.5k
    if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)
8397
1
        MarkIniSettingsDirty(window);
8398
80.5k
}
8399
8400
void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)
8401
0
{
8402
0
    SetWindowSize(GImGui->CurrentWindow, size, cond);
8403
0
}
8404
8405
void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
8406
0
{
8407
0
    if (ImGuiWindow* window = FindWindowByName(name))
8408
0
        SetWindowSize(window, size, cond);
8409
0
}
8410
8411
void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
8412
0
{
8413
    // Test condition (NB: bit 0 is always true) and clear flags for next time
8414
0
    if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)
8415
0
        return;
8416
0
    window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);
8417
8418
    // Queue applying in Begin()
8419
0
    if (window->WantCollapseToggle)
8420
0
        window->Collapsed ^= 1;
8421
0
    window->WantCollapseToggle = (window->Collapsed != collapsed);
8422
0
}
8423
8424
void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
8425
0
{
8426
0
    IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
8427
0
    window->HitTestHoleSize = ImVec2ih(size);
8428
0
    window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);
8429
0
}
8430
8431
void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window)
8432
0
{
8433
0
    window->Hidden = window->SkipItems = true;
8434
0
    window->HiddenFramesCanSkipItems = 1;
8435
0
}
8436
8437
void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
8438
0
{
8439
0
    SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
8440
0
}
8441
8442
bool ImGui::IsWindowCollapsed()
8443
0
{
8444
0
    ImGuiWindow* window = GetCurrentWindowRead();
8445
0
    return window->Collapsed;
8446
0
}
8447
8448
bool ImGui::IsWindowAppearing()
8449
0
{
8450
0
    ImGuiWindow* window = GetCurrentWindowRead();
8451
0
    return window->Appearing;
8452
0
}
8453
8454
void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)
8455
0
{
8456
0
    if (ImGuiWindow* window = FindWindowByName(name))
8457
0
        SetWindowCollapsed(window, collapsed, cond);
8458
0
}
8459
8460
void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)
8461
81.2k
{
8462
81.2k
    ImGuiContext& g = *GImGui;
8463
81.2k
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8464
81.2k
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasPos;
8465
81.2k
    g.NextWindowData.PosVal = pos;
8466
81.2k
    g.NextWindowData.PosPivotVal = pivot;
8467
81.2k
    g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
8468
81.2k
}
8469
8470
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
8471
161k
{
8472
161k
    ImGuiContext& g = *GImGui;
8473
161k
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8474
161k
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSize;
8475
161k
    g.NextWindowData.SizeVal = size;
8476
161k
    g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;
8477
161k
}
8478
8479
// For each axis:
8480
// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.
8481
// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.
8482
// - See "Demo->Examples->Constrained-resizing window" for examples.
8483
void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)
8484
0
{
8485
0
    ImGuiContext& g = *GImGui;
8486
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
8487
0
    g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);
8488
0
    g.NextWindowData.SizeCallback = custom_callback;
8489
0
    g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;
8490
0
}
8491
8492
// Content size = inner scrollable rectangle, padded with WindowPadding.
8493
// SetNextWindowContentSize(ImVec2(100,100)) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.
8494
void ImGui::SetNextWindowContentSize(const ImVec2& size)
8495
0
{
8496
0
    ImGuiContext& g = *GImGui;
8497
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasContentSize;
8498
0
    g.NextWindowData.ContentSizeVal = ImTrunc(size);
8499
0
}
8500
8501
void ImGui::SetNextWindowScroll(const ImVec2& scroll)
8502
0
{
8503
0
    ImGuiContext& g = *GImGui;
8504
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasScroll;
8505
0
    g.NextWindowData.ScrollVal = scroll;
8506
0
}
8507
8508
void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)
8509
0
{
8510
0
    ImGuiContext& g = *GImGui;
8511
0
    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.
8512
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasCollapsed;
8513
0
    g.NextWindowData.CollapsedVal = collapsed;
8514
0
    g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;
8515
0
}
8516
8517
void ImGui::SetNextWindowBgAlpha(float alpha)
8518
0
{
8519
0
    ImGuiContext& g = *GImGui;
8520
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasBgAlpha;
8521
0
    g.NextWindowData.BgAlphaVal = alpha;
8522
0
}
8523
8524
// This is experimental and meant to be a toy for exploring a future/wider range of features.
8525
void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags)
8526
0
{
8527
0
    ImGuiContext& g = *GImGui;
8528
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasRefreshPolicy;
8529
0
    g.NextWindowData.RefreshFlagsVal = flags;
8530
0
}
8531
8532
ImDrawList* ImGui::GetWindowDrawList()
8533
0
{
8534
0
    ImGuiWindow* window = GetCurrentWindow();
8535
0
    return window->DrawList;
8536
0
}
8537
8538
ImFont* ImGui::GetFont()
8539
0
{
8540
0
    return GImGui->Font;
8541
0
}
8542
8543
ImFontBaked* ImGui::GetFontBaked()
8544
0
{
8545
0
    return GImGui->FontBaked;
8546
0
}
8547
8548
// Get current font size (= height in pixels) of current font, with global scale factors applied.
8549
// - Use style.FontSizeBase to get value before global scale factors.
8550
// - recap: ImGui::GetFontSize() == style.FontSizeBase * (style.FontScaleMain * style.FontScaleDpi * other_scaling_factors)
8551
float ImGui::GetFontSize()
8552
0
{
8553
0
    return GImGui->FontSize;
8554
0
}
8555
8556
ImVec2 ImGui::GetFontTexUvWhitePixel()
8557
0
{
8558
0
    return GImGui->DrawListSharedData.TexUvWhitePixel;
8559
0
}
8560
8561
// Prefer using PushFont(NULL, style.FontSizeBase * factor), or use style.FontScaleMain to scale all windows.
8562
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8563
void ImGui::SetWindowFontScale(float scale)
8564
0
{
8565
0
    IM_ASSERT(scale > 0.0f);
8566
0
    ImGuiWindow* window = GetCurrentWindow();
8567
0
    window->FontWindowScale = scale;
8568
0
    UpdateCurrentFontSize(0.0f);
8569
0
}
8570
#endif
8571
8572
void ImGui::PushFocusScope(ImGuiID id)
8573
161k
{
8574
161k
    ImGuiContext& g = *GImGui;
8575
161k
    ImGuiFocusScopeData data;
8576
161k
    data.ID = id;
8577
161k
    data.WindowID = g.CurrentWindow->ID;
8578
161k
    g.FocusScopeStack.push_back(data);
8579
161k
    g.CurrentFocusScopeId = id;
8580
161k
}
8581
8582
void ImGui::PopFocusScope()
8583
161k
{
8584
161k
    ImGuiContext& g = *GImGui;
8585
161k
    if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack)
8586
0
    {
8587
0
        IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!");
8588
0
        return;
8589
0
    }
8590
161k
    g.FocusScopeStack.pop_back();
8591
161k
    g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0;
8592
161k
}
8593
8594
void ImGui::SetNavFocusScope(ImGuiID focus_scope_id)
8595
1.07k
{
8596
1.07k
    ImGuiContext& g = *GImGui;
8597
1.07k
    g.NavFocusScopeId = focus_scope_id;
8598
1.07k
    g.NavFocusRoute.resize(0); // Invalidate
8599
1.07k
    if (focus_scope_id == 0)
8600
2
        return;
8601
1.07k
    IM_ASSERT(g.NavWindow != NULL);
8602
8603
    // Store current path (in reverse order)
8604
1.07k
    if (focus_scope_id == g.CurrentFocusScopeId)
8605
1.07k
    {
8606
        // Top of focus stack contains local focus scopes inside current window
8607
2.14k
        for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--)
8608
1.07k
            g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]);
8609
1.07k
    }
8610
4
    else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId)
8611
4
        g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID });
8612
0
    else
8613
0
        return;
8614
8615
    // Then follow on manually set ParentWindowForFocusRoute field (#6798)
8616
1.07k
    for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute)
8617
0
        g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID });
8618
1.07k
    IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3
8619
1.07k
}
8620
8621
// Focus = move navigation cursor, set scrolling, set focus window.
8622
void ImGui::FocusItem()
8623
0
{
8624
0
    ImGuiContext& g = *GImGui;
8625
0
    ImGuiWindow* window = g.CurrentWindow;
8626
0
    IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name);
8627
0
    if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?
8628
0
    {
8629
0
        IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n");
8630
0
        return;
8631
0
    }
8632
8633
0
    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible | ImGuiNavMoveFlags_NoSelect;
8634
0
    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8635
0
    SetNavWindow(window);
8636
0
    NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);
8637
0
    NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8638
0
}
8639
8640
void ImGui::ActivateItemByID(ImGuiID id)
8641
0
{
8642
0
    ImGuiContext& g = *GImGui;
8643
0
    g.NavNextActivateId = id;
8644
0
    g.NavNextActivateFlags = ImGuiActivateFlags_None;
8645
0
}
8646
8647
// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!
8648
// But ActivateItem() should function without altering scroll/focus?
8649
void ImGui::SetKeyboardFocusHere(int offset)
8650
0
{
8651
0
    ImGuiContext& g = *GImGui;
8652
0
    ImGuiWindow* window = g.CurrentWindow;
8653
0
    IM_ASSERT(offset >= -1);    // -1 is allowed but not below
8654
0
    IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name);
8655
8656
    // It makes sense in the vast majority of cases to never interrupt a drag and drop.
8657
    // When we refactor this function into ActivateItem() we may want to make this an option.
8658
    // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but
8659
    // is also automatically dropped in the event g.ActiveId is stolen.
8660
0
    if (g.DragDropActive || g.MovingWindow != NULL)
8661
0
    {
8662
0
        IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n");
8663
0
        return;
8664
0
    }
8665
8666
0
    SetNavWindow(window);
8667
8668
0
    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavCursorVisible;
8669
0
    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
8670
0
    NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
8671
0
    if (offset == -1)
8672
0
    {
8673
0
        NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);
8674
0
    }
8675
0
    else
8676
0
    {
8677
0
        g.NavTabbingDir = 1;
8678
0
        g.NavTabbingCounter = offset + 1;
8679
0
    }
8680
0
}
8681
8682
void ImGui::SetItemDefaultFocus()
8683
0
{
8684
0
    ImGuiContext& g = *GImGui;
8685
0
    ImGuiWindow* window = g.CurrentWindow;
8686
0
    if (!window->Appearing)
8687
0
        return;
8688
0
    if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)
8689
0
        return;
8690
8691
0
    g.NavInitRequest = false;
8692
0
    NavApplyItemToResult(&g.NavInitResult);
8693
0
    NavUpdateAnyRequestFlag();
8694
8695
    // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)
8696
0
    if (!window->ClipRect.Contains(g.LastItemData.Rect))
8697
0
        ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);
8698
0
}
8699
8700
void ImGui::SetStateStorage(ImGuiStorage* tree)
8701
0
{
8702
0
    ImGuiWindow* window = GImGui->CurrentWindow;
8703
0
    window->DC.StateStorage = tree ? tree : &window->StateStorage;
8704
0
}
8705
8706
ImGuiStorage* ImGui::GetStateStorage()
8707
0
{
8708
0
    ImGuiWindow* window = GImGui->CurrentWindow;
8709
0
    return window->DC.StateStorage;
8710
0
}
8711
8712
bool ImGui::IsRectVisible(const ImVec2& size)
8713
0
{
8714
0
    ImGuiWindow* window = GImGui->CurrentWindow;
8715
0
    return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));
8716
0
}
8717
8718
bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)
8719
0
{
8720
0
    ImGuiWindow* window = GImGui->CurrentWindow;
8721
0
    return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));
8722
0
}
8723
8724
//-----------------------------------------------------------------------------
8725
// [SECTION] FONTS, TEXTURES
8726
//-----------------------------------------------------------------------------
8727
// Most of the relevant font logic is in imgui_draw.cpp.
8728
// Those are high-level support functions.
8729
//-----------------------------------------------------------------------------
8730
// - UpdateTexturesNewFrame() [Internal]
8731
// - UpdateTexturesEndFrame() [Internal]
8732
// - UpdateFontsNewFrame() [Internal]
8733
// - UpdateFontsEndFrame() [Internal]
8734
// - GetDefaultFont() [Internal]
8735
// - RegisterUserTexture() [Internal]
8736
// - UnregisterUserTexture() [Internal]
8737
// - RegisterFontAtlas() [Internal]
8738
// - UnregisterFontAtlas() [Internal]
8739
// - SetCurrentFont() [Internal]
8740
// - UpdateCurrentFontSize() [Internal]
8741
// - SetFontRasterizerDensity() [Internal]
8742
// - PushFont()
8743
// - PopFont()
8744
//-----------------------------------------------------------------------------
8745
8746
static void ImGui::UpdateTexturesNewFrame()
8747
80.5k
{
8748
    // Cannot update every atlases based on atlas's FrameCount < g.FrameCount, because an atlas may be shared by multiple contexts with different frame count.
8749
80.5k
    ImGuiContext& g = *GImGui;
8750
80.5k
    const bool has_textures = (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
8751
80.5k
    for (ImFontAtlas* atlas : g.FontAtlases)
8752
80.5k
    {
8753
80.5k
        if (atlas->OwnerContext == &g)
8754
80.5k
        {
8755
80.5k
            ImFontAtlasUpdateNewFrame(atlas, g.FrameCount, has_textures);
8756
80.5k
        }
8757
0
        else
8758
0
        {
8759
            // (1) If you manage font atlases yourself, e.g. create a ImFontAtlas yourself you need to call ImFontAtlasUpdateNewFrame() on it.
8760
            // Otherwise, calling ImGui::CreateContext() without parameter will create an atlas owned by the context.
8761
            // (2) If you have multiple font atlases, make sure the 'atlas->RendererHasTextures' as specified in the ImFontAtlasUpdateNewFrame() call matches for that.
8762
            // (3) If you have multiple imgui contexts, they also need to have a matching value for ImGuiBackendFlags_RendererHasTextures.
8763
0
            IM_ASSERT(atlas->Builder != NULL && atlas->Builder->FrameCount != -1);
8764
0
            IM_ASSERT(atlas->RendererHasTextures == has_textures);
8765
0
        }
8766
80.5k
    }
8767
80.5k
}
8768
8769
// Build a single texture list
8770
static void ImGui::UpdateTexturesEndFrame()
8771
80.5k
{
8772
80.5k
    ImGuiContext& g = *GImGui;
8773
80.5k
    g.PlatformIO.Textures.resize(0);
8774
80.5k
    for (ImFontAtlas* atlas : g.FontAtlases)
8775
80.5k
        for (ImTextureData* tex : atlas->TexList)
8776
80.5k
        {
8777
            // We provide this information so backends can decide whether to destroy textures.
8778
            // This means in practice that if N imgui contexts are created with a shared atlas, we assume all of them have a backend initialized.
8779
80.5k
            tex->RefCount = (unsigned short)atlas->RefCount;
8780
80.5k
            g.PlatformIO.Textures.push_back(tex);
8781
80.5k
        }
8782
80.5k
    for (ImTextureData* tex : g.UserTextures)
8783
0
        g.PlatformIO.Textures.push_back(tex);
8784
80.5k
}
8785
8786
void ImGui::UpdateFontsNewFrame()
8787
80.5k
{
8788
80.5k
    ImGuiContext& g = *GImGui;
8789
80.5k
    if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8790
0
        for (ImFontAtlas* atlas : g.FontAtlases)
8791
0
            atlas->Locked = true;
8792
8793
80.5k
    if (g.Style._NextFrameFontSizeBase != 0.0f)
8794
0
    {
8795
0
        g.Style.FontSizeBase = g.Style._NextFrameFontSizeBase;
8796
0
        g.Style._NextFrameFontSizeBase = 0.0f;
8797
0
    }
8798
8799
    // Apply default font size the first time
8800
80.5k
    ImFont* font = ImGui::GetDefaultFont();
8801
80.5k
    if (g.Style.FontSizeBase <= 0.0f)
8802
1
        g.Style.FontSizeBase = (font->LegacySize > 0.0f ? font->LegacySize : FONT_DEFAULT_SIZE);
8803
8804
    // Set initial font
8805
80.5k
    g.Font = font;
8806
80.5k
    g.FontSizeBase = g.Style.FontSizeBase;
8807
80.5k
    g.FontSize = 0.0f;
8808
80.5k
    ImFontStackData font_stack_data = { font, g.Style.FontSizeBase, g.Style.FontSizeBase };           // <--- Will restore FontSize
8809
80.5k
    SetCurrentFont(font_stack_data.Font, font_stack_data.FontSizeBeforeScaling, 0.0f); // <--- but use 0.0f to enable scale
8810
80.5k
    g.FontStack.push_back(font_stack_data);
8811
80.5k
    IM_ASSERT(g.Font->IsLoaded());
8812
80.5k
}
8813
8814
void ImGui::UpdateFontsEndFrame()
8815
80.5k
{
8816
80.5k
    PopFont();
8817
80.5k
}
8818
8819
ImFont* ImGui::GetDefaultFont()
8820
80.5k
{
8821
80.5k
    ImGuiContext& g = *GImGui;
8822
80.5k
    ImFontAtlas* atlas = g.IO.Fonts;
8823
80.5k
    if (atlas->Builder == NULL || atlas->Fonts.Size == 0)
8824
0
        ImFontAtlasBuildMain(atlas);
8825
80.5k
    return g.IO.FontDefault ? g.IO.FontDefault : atlas->Fonts[0];
8826
80.5k
}
8827
8828
// EXPERIMENTAL: DO NOT USE YET.
8829
void ImGui::RegisterUserTexture(ImTextureData* tex)
8830
0
{
8831
0
    ImGuiContext& g = *GImGui;
8832
0
    tex->RefCount++;
8833
0
    g.UserTextures.push_back(tex);
8834
0
}
8835
8836
void ImGui::UnregisterUserTexture(ImTextureData* tex)
8837
0
{
8838
0
    ImGuiContext& g = *GImGui;
8839
0
    IM_ASSERT(tex->RefCount > 0);
8840
0
    tex->RefCount--;
8841
0
    g.UserTextures.find_erase(tex);
8842
0
}
8843
8844
void ImGui::RegisterFontAtlas(ImFontAtlas* atlas)
8845
1
{
8846
1
    ImGuiContext& g = *GImGui;
8847
1
    if (g.FontAtlases.Size == 0)
8848
1
        IM_ASSERT(atlas == g.IO.Fonts);
8849
1
    atlas->RefCount++;
8850
1
    g.FontAtlases.push_back(atlas);
8851
1
    ImFontAtlasAddDrawListSharedData(atlas, &g.DrawListSharedData);
8852
1
    for (ImTextureData* tex : atlas->TexList)
8853
0
        tex->RefCount = (unsigned short)atlas->RefCount;
8854
1
}
8855
8856
void ImGui::UnregisterFontAtlas(ImFontAtlas* atlas)
8857
1
{
8858
1
    ImGuiContext& g = *GImGui;
8859
1
    IM_ASSERT(atlas->RefCount > 0);
8860
1
    ImFontAtlasRemoveDrawListSharedData(atlas, &g.DrawListSharedData);
8861
1
    g.FontAtlases.find_erase(atlas);
8862
1
    atlas->RefCount--;
8863
1
    for (ImTextureData* tex : atlas->TexList)
8864
1
        tex->RefCount = (unsigned short)atlas->RefCount;
8865
1
}
8866
8867
// Use ImDrawList::_SetTexture(), making our shared g.FontStack[] authoritative against window-local ImDrawList.
8868
// - Whereas ImDrawList::PushTexture()/PopTexture() is not to be used across Begin() calls.
8869
// - Note that we don't propagate current texture id when e.g. Begin()-ing into a new window, we never really did...
8870
//   - Some code paths never really fully worked with multiple atlas textures.
8871
//   - The right-ish solution may be to remove _SetTexture() and make AddText/RenderText lazily call PushTexture()/PopTexture()
8872
//     the same way AddImage() does, but then all other primitives would also need to? I don't think we should tackle this problem
8873
//     because we have a concrete need and a test bed for multiple atlas textures.
8874
// FIXME-NEWATLAS-V2: perhaps we can now leverage ImFontAtlasUpdateDrawListsTextures() ?
8875
void ImGui::SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling)
8876
161k
{
8877
161k
    ImGuiContext& g = *GImGui;
8878
161k
    g.Font = font;
8879
161k
    g.FontSizeBase = font_size_before_scaling;
8880
161k
    UpdateCurrentFontSize(font_size_after_scaling);
8881
8882
161k
    if (font != NULL)
8883
161k
    {
8884
161k
        IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?
8885
161k
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8886
161k
        IM_ASSERT(font->Scale > 0.0f);
8887
161k
#endif
8888
161k
        ImFontAtlas* atlas = font->ContainerAtlas;
8889
161k
        g.DrawListSharedData.FontAtlas = atlas;
8890
161k
        g.DrawListSharedData.Font = font;
8891
161k
        ImFontAtlasUpdateDrawListsSharedData(atlas);
8892
161k
        if (g.CurrentWindow != NULL)
8893
0
            g.CurrentWindow->DrawList->_SetTexture(atlas->TexRef);
8894
161k
    }
8895
161k
}
8896
8897
void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling)
8898
404k
{
8899
404k
    ImGuiContext& g = *GImGui;
8900
404k
    ImGuiWindow* window = g.CurrentWindow;
8901
8902
404k
    g.Style.FontSizeBase = g.FontSizeBase;
8903
8904
    // Early out to avoid hidden window keeping bakes referenced and out of GC reach.
8905
    // However this would leave a pretty subtle and damning error surface area if g.FontBaked was mismatching.
8906
    // FIXME: perhaps g.FontSize should be updated?
8907
404k
    if (window != NULL && window->SkipItems)
8908
0
    {
8909
0
        ImGuiTable* table = g.CurrentTable;
8910
0
        if (table == NULL || (table->CurrentColumn != -1 && table->Columns[table->CurrentColumn].IsSkipItems == false)) // See 8465#issuecomment-2951509561 and #8865. Ideally the SkipItems=true in tables would be amended with extra data.
8911
0
            return;
8912
0
    }
8913
8914
    // Restoring is pretty much only used by PopFont()
8915
404k
    float final_size = (restore_font_size_after_scaling > 0.0f) ? restore_font_size_after_scaling : 0.0f;
8916
404k
    if (final_size == 0.0f)
8917
323k
    {
8918
323k
        final_size = g.FontSizeBase;
8919
8920
        // Global scale factors
8921
323k
        final_size *= g.Style.FontScaleMain;    // Main global scale factor
8922
323k
        final_size *= g.Style.FontScaleDpi;     // Per-monitor/viewport DPI scale factor, automatically updated when io.ConfigDpiScaleFonts is enabled.
8923
8924
        // Window scale (mostly obsolete now)
8925
323k
        if (window != NULL)
8926
243k
            final_size *= window->FontWindowScale;
8927
8928
        // Legacy scale factors
8929
323k
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8930
323k
        final_size *= g.IO.FontGlobalScale; // Use style.FontScaleMain instead!
8931
323k
        if (g.Font != NULL)
8932
323k
            final_size *= g.Font->Scale;    // Was never really useful.
8933
323k
#endif
8934
323k
    }
8935
8936
    // Round font size
8937
    // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.
8938
    // - We may support it better later and remove this rounding.
8939
404k
    final_size = GetRoundedFontSize(final_size);
8940
404k
    final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX);
8941
404k
    if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures))
8942
404k
        g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity;
8943
404k
    g.FontSize = final_size;
8944
404k
    g.FontBaked = (g.Font != NULL && window != NULL) ? g.Font->GetFontBaked(final_size) : NULL;
8945
404k
    g.FontBakedScale = (g.Font != NULL && window != NULL) ? (g.FontSize / g.FontBaked->Size) : 0.0f;
8946
404k
    g.DrawListSharedData.FontSize = g.FontSize;
8947
404k
    g.DrawListSharedData.FontScale = g.FontBakedScale;
8948
404k
}
8949
8950
// Exposed in case user may want to override setting density.
8951
// IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it.
8952
void ImGui::SetFontRasterizerDensity(float rasterizer_density)
8953
0
{
8954
0
    ImGuiContext& g = *GImGui;
8955
0
    IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures);
8956
0
    if (g.FontRasterizerDensity == rasterizer_density)
8957
0
        return;
8958
0
    g.FontRasterizerDensity = rasterizer_density;
8959
0
    UpdateCurrentFontSize(0.0f);
8960
0
}
8961
8962
// If you want to scale an existing font size! Read comments in imgui.h!
8963
void ImGui::PushFont(ImFont* font, float font_size_base)
8964
0
{
8965
0
    ImGuiContext& g = *GImGui;
8966
0
    if (font == NULL) // Before 1.92 (June 2025), PushFont(NULL) == PushFont(GetDefaultFont())
8967
0
        font = g.Font;
8968
0
    IM_ASSERT(font != NULL);
8969
0
    IM_ASSERT(font_size_base >= 0.0f);
8970
8971
0
    g.FontStack.push_back({ g.Font, g.FontSizeBase, g.FontSize });
8972
0
    if (font_size_base == 0.0f)
8973
0
        font_size_base = g.FontSizeBase; // Keep current font size
8974
0
    SetCurrentFont(font, font_size_base, 0.0f);
8975
0
}
8976
8977
void  ImGui::PopFont()
8978
80.5k
{
8979
80.5k
    ImGuiContext& g = *GImGui;
8980
80.5k
    if (g.FontStack.Size <= 0)
8981
0
    {
8982
0
        IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!");
8983
0
        return;
8984
0
    }
8985
80.5k
    ImFontStackData* font_stack_data = &g.FontStack.back();
8986
80.5k
    SetCurrentFont(font_stack_data->Font, font_stack_data->FontSizeBeforeScaling, font_stack_data->FontSizeAfterScaling);
8987
80.5k
    g.FontStack.pop_back();
8988
80.5k
}
8989
8990
//-----------------------------------------------------------------------------
8991
// [SECTION] ID STACK
8992
//-----------------------------------------------------------------------------
8993
8994
// This is one of the very rare legacy case where we use ImGuiWindow methods,
8995
// it should ideally be flattened at some point but it's been used a lots by widgets.
8996
IM_MSVC_RUNTIME_CHECKS_OFF
8997
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
8998
890k
{
8999
890k
    ImGuiID seed = IDStack.back();
9000
890k
    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9001
890k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9002
890k
    ImGuiContext& g = *Ctx;
9003
890k
    if (g.DebugHookIdInfoId == id)
9004
0
        ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9005
890k
#endif
9006
890k
    return id;
9007
890k
}
9008
9009
ImGuiID ImGuiWindow::GetID(const void* ptr)
9010
0
{
9011
0
    ImGuiID seed = IDStack.back();
9012
0
    ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
9013
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9014
0
    ImGuiContext& g = *Ctx;
9015
0
    if (g.DebugHookIdInfoId == id)
9016
0
        ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);
9017
0
#endif
9018
0
    return id;
9019
0
}
9020
9021
ImGuiID ImGuiWindow::GetID(int n)
9022
0
{
9023
0
    ImGuiID seed = IDStack.back();
9024
0
    ImGuiID id = ImHashData(&n, sizeof(n), seed);
9025
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9026
0
    ImGuiContext& g = *Ctx;
9027
0
    if (g.DebugHookIdInfoId == id)
9028
0
        ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9029
0
#endif
9030
0
    return id;
9031
0
}
9032
9033
// This is only used in rare/specific situations to manufacture an ID out of nowhere.
9034
// FIXME: Consider instead storing last non-zero ID + count of successive zero-ID, and combine those?
9035
ImGuiID ImGuiWindow::GetIDFromPos(const ImVec2& p_abs)
9036
0
{
9037
0
    ImGuiID seed = IDStack.back();
9038
0
    ImVec2 p_rel = ImGui::WindowPosAbsToRel(this, p_abs);
9039
0
    ImGuiID id = ImHashData(&p_rel, sizeof(p_rel), seed);
9040
0
    return id;
9041
0
}
9042
9043
// "
9044
ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
9045
0
{
9046
0
    ImGuiID seed = IDStack.back();
9047
0
    ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);
9048
0
    ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
9049
0
    return id;
9050
0
}
9051
9052
void ImGui::PushID(const char* str_id)
9053
324k
{
9054
324k
    ImGuiContext& g = *GImGui;
9055
324k
    ImGuiWindow* window = g.CurrentWindow;
9056
324k
    ImGuiID id = window->GetID(str_id);
9057
324k
    window->IDStack.push_back(id);
9058
324k
}
9059
9060
void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
9061
0
{
9062
0
    ImGuiContext& g = *GImGui;
9063
0
    ImGuiWindow* window = g.CurrentWindow;
9064
0
    ImGuiID id = window->GetID(str_id_begin, str_id_end);
9065
0
    window->IDStack.push_back(id);
9066
0
}
9067
9068
void ImGui::PushID(const void* ptr_id)
9069
0
{
9070
0
    ImGuiContext& g = *GImGui;
9071
0
    ImGuiWindow* window = g.CurrentWindow;
9072
0
    ImGuiID id = window->GetID(ptr_id);
9073
0
    window->IDStack.push_back(id);
9074
0
}
9075
9076
void ImGui::PushID(int int_id)
9077
0
{
9078
0
    ImGuiContext& g = *GImGui;
9079
0
    ImGuiWindow* window = g.CurrentWindow;
9080
0
    ImGuiID id = window->GetID(int_id);
9081
0
    window->IDStack.push_back(id);
9082
0
}
9083
9084
// Push a given id value ignoring the ID stack as a seed.
9085
void ImGui::PushOverrideID(ImGuiID id)
9086
0
{
9087
0
    ImGuiContext& g = *GImGui;
9088
0
    ImGuiWindow* window = g.CurrentWindow;
9089
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9090
0
    if (g.DebugHookIdInfoId == id)
9091
0
        DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);
9092
0
#endif
9093
0
    window->IDStack.push_back(id);
9094
0
}
9095
9096
// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call
9097
// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.
9098
//  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)
9099
ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)
9100
0
{
9101
0
    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
9102
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9103
0
    ImGuiContext& g = *GImGui;
9104
0
    if (g.DebugHookIdInfoId == id)
9105
0
        DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);
9106
0
#endif
9107
0
    return id;
9108
0
}
9109
9110
ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)
9111
0
{
9112
0
    ImGuiID id = ImHashData(&n, sizeof(n), seed);
9113
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
9114
0
    ImGuiContext& g = *GImGui;
9115
0
    if (g.DebugHookIdInfoId == id)
9116
0
        DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);
9117
0
#endif
9118
0
    return id;
9119
0
}
9120
9121
void ImGui::PopID()
9122
324k
{
9123
324k
    ImGuiWindow* window = GImGui->CurrentWindow;
9124
324k
    if (window->IDStack.Size <= 1)
9125
0
    {
9126
0
        IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!");
9127
0
        return;
9128
0
    }
9129
324k
    window->IDStack.pop_back();
9130
324k
}
9131
9132
ImGuiID ImGui::GetID(const char* str_id)
9133
0
{
9134
0
    ImGuiWindow* window = GImGui->CurrentWindow;
9135
0
    return window->GetID(str_id);
9136
0
}
9137
9138
ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)
9139
0
{
9140
0
    ImGuiWindow* window = GImGui->CurrentWindow;
9141
0
    return window->GetID(str_id_begin, str_id_end);
9142
0
}
9143
9144
ImGuiID ImGui::GetID(const void* ptr_id)
9145
0
{
9146
0
    ImGuiWindow* window = GImGui->CurrentWindow;
9147
0
    return window->GetID(ptr_id);
9148
0
}
9149
9150
ImGuiID ImGui::GetID(int int_id)
9151
0
{
9152
0
    ImGuiWindow* window = GImGui->CurrentWindow;
9153
0
    return window->GetID(int_id);
9154
0
}
9155
IM_MSVC_RUNTIME_CHECKS_RESTORE
9156
9157
//-----------------------------------------------------------------------------
9158
// [SECTION] INPUTS
9159
//-----------------------------------------------------------------------------
9160
// - GetModForLRModKey() [Internal]
9161
// - FixupKeyChord() [Internal]
9162
// - GetKeyData() [Internal]
9163
// - GetKeyIndex() [Internal]
9164
// - GetKeyName()
9165
// - GetKeyChordName() [Internal]
9166
// - CalcTypematicRepeatAmount() [Internal]
9167
// - GetTypematicRepeatRate() [Internal]
9168
// - GetKeyPressedAmount() [Internal]
9169
// - GetKeyMagnitude2d() [Internal]
9170
//-----------------------------------------------------------------------------
9171
// - UpdateKeyRoutingTable() [Internal]
9172
// - GetRoutingIdFromOwnerId() [Internal]
9173
// - GetShortcutRoutingData() [Internal]
9174
// - CalcRoutingScore() [Internal]
9175
// - SetShortcutRouting() [Internal]
9176
// - TestShortcutRouting() [Internal]
9177
//-----------------------------------------------------------------------------
9178
// - IsKeyDown()
9179
// - IsKeyPressed()
9180
// - IsKeyReleased()
9181
//-----------------------------------------------------------------------------
9182
// - IsMouseDown()
9183
// - IsMouseClicked()
9184
// - IsMouseReleased()
9185
// - IsMouseDoubleClicked()
9186
// - GetMouseClickedCount()
9187
// - IsMouseHoveringRect() [Internal]
9188
// - IsMouseDragPastThreshold() [Internal]
9189
// - IsMouseDragging()
9190
// - GetMousePos()
9191
// - SetMousePos() [Internal]
9192
// - GetMousePosOnOpeningCurrentPopup()
9193
// - IsMousePosValid()
9194
// - IsAnyMouseDown()
9195
// - GetMouseDragDelta()
9196
// - ResetMouseDragDelta()
9197
// - GetMouseCursor()
9198
// - SetMouseCursor()
9199
//-----------------------------------------------------------------------------
9200
// - UpdateAliasKey()
9201
// - GetMergedModsFromKeys()
9202
// - UpdateKeyboardInputs()
9203
// - UpdateMouseInputs()
9204
//-----------------------------------------------------------------------------
9205
// - LockWheelingWindow [Internal]
9206
// - FindBestWheelingWindow [Internal]
9207
// - UpdateMouseWheel() [Internal]
9208
//-----------------------------------------------------------------------------
9209
// - SetNextFrameWantCaptureKeyboard()
9210
// - SetNextFrameWantCaptureMouse()
9211
//-----------------------------------------------------------------------------
9212
// - GetInputSourceName() [Internal]
9213
// - DebugPrintInputEvent() [Internal]
9214
// - UpdateInputEvents() [Internal]
9215
//-----------------------------------------------------------------------------
9216
// - GetKeyOwner() [Internal]
9217
// - TestKeyOwner() [Internal]
9218
// - SetKeyOwner() [Internal]
9219
// - SetItemKeyOwner() [Internal]
9220
// - Shortcut() [Internal]
9221
//-----------------------------------------------------------------------------
9222
9223
static ImGuiKeyChord GetModForLRModKey(ImGuiKey key)
9224
0
{
9225
0
    if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl)
9226
0
        return ImGuiMod_Ctrl;
9227
0
    if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift)
9228
0
        return ImGuiMod_Shift;
9229
0
    if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt)
9230
0
        return ImGuiMod_Alt;
9231
0
    if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper)
9232
0
        return ImGuiMod_Super;
9233
0
    return ImGuiMod_None;
9234
0
}
9235
9236
ImGuiKeyChord ImGui::FixupKeyChord(ImGuiKeyChord key_chord)
9237
322k
{
9238
    // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9239
322k
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9240
322k
    if (IsLRModKey(key))
9241
0
        key_chord |= GetModForLRModKey(key);
9242
322k
    return key_chord;
9243
322k
}
9244
9245
ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)
9246
1.87M
{
9247
1.87M
    ImGuiContext& g = *ctx;
9248
9249
    // Special storage location for mods
9250
1.87M
    if (key & ImGuiMod_Mask_)
9251
653k
        key = ConvertSingleModFlagToKey(key);
9252
9253
1.87M
    IM_ASSERT(IsNamedKey(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.");
9254
1.87M
    return &g.IO.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9255
1.87M
}
9256
9257
// Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.
9258
static const char* const GKeyNames[] =
9259
{
9260
    "Tab", "LeftArrow", "RightArrow", "UpArrow", "DownArrow", "PageUp", "PageDown",
9261
    "Home", "End", "Insert", "Delete", "Backspace", "Space", "Enter", "Escape",
9262
    "LeftCtrl", "LeftShift", "LeftAlt", "LeftSuper", "RightCtrl", "RightShift", "RightAlt", "RightSuper", "Menu",
9263
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
9264
    "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
9265
    "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
9266
    "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24",
9267
    "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket",
9268
    "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen",
9269
    "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6",
9270
    "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply",
9271
    "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual",
9272
    "AppBack", "AppForward", "Oem102",
9273
    "GamepadStart", "GamepadBack",
9274
    "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown",
9275
    "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown",
9276
    "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3",
9277
    "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown",
9278
    "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown",
9279
    "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY",
9280
    "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names.
9281
};
9282
IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));
9283
9284
const char* ImGui::GetKeyName(ImGuiKey key)
9285
0
{
9286
0
    if (key == ImGuiKey_None)
9287
0
        return "None";
9288
0
    IM_ASSERT(IsNamedKeyOrMod(key) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.");
9289
0
    if (key & ImGuiMod_Mask_)
9290
0
        key = ConvertSingleModFlagToKey(key);
9291
0
    if (!IsNamedKey(key))
9292
0
        return "Unknown";
9293
9294
0
    return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];
9295
0
}
9296
9297
// Return untranslated names: on macOS, Cmd key will show as Ctrl, Ctrl key will show as super.
9298
// Lifetime of return value: valid until next call to same function.
9299
const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord)
9300
0
{
9301
0
    ImGuiContext& g = *GImGui;
9302
9303
0
    const ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9304
0
    if (IsLRModKey(key))
9305
0
        key_chord &= ~GetModForLRModKey(key); // Return "Ctrl+LeftShift" instead of "Ctrl+Shift+LeftShift"
9306
0
    ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s",
9307
0
        (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "",
9308
0
        (key_chord & ImGuiMod_Shift) ? "Shift+" : "",
9309
0
        (key_chord & ImGuiMod_Alt) ? "Alt+" : "",
9310
0
        (key_chord & ImGuiMod_Super) ? "Super+" : "",
9311
0
        (key != ImGuiKey_None || key_chord == ImGuiKey_None) ? GetKeyName(key) : "");
9312
0
    size_t len;
9313
0
    if (key == ImGuiKey_None && key_chord != 0)
9314
0
        if ((len = ImStrlen(g.TempKeychordName)) != 0) // Remove trailing '+'
9315
0
            g.TempKeychordName[len - 1] = 0;
9316
0
    return g.TempKeychordName;
9317
0
}
9318
9319
// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)
9320
// t1 = current time (e.g.: g.Time)
9321
// An event is triggered at:
9322
//  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N
9323
int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)
9324
0
{
9325
0
    if (t1 == 0.0f)
9326
0
        return 1;
9327
0
    if (t0 >= t1)
9328
0
        return 0;
9329
0
    if (repeat_rate <= 0.0f)
9330
0
        return (t0 < repeat_delay) && (t1 >= repeat_delay);
9331
0
    const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);
9332
0
    const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);
9333
0
    const int count = count_t1 - count_t0;
9334
0
    return count;
9335
0
}
9336
9337
void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)
9338
0
{
9339
0
    ImGuiContext& g = *GImGui;
9340
0
    switch (flags & ImGuiInputFlags_RepeatRateMask_)
9341
0
    {
9342
0
    case ImGuiInputFlags_RepeatRateNavMove:             *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;
9343
0
    case ImGuiInputFlags_RepeatRateNavTweak:            *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;
9344
0
    case ImGuiInputFlags_RepeatRateDefault: default:    *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;
9345
0
    }
9346
0
}
9347
9348
// Return value representing the number of presses in the last time period, for the given repeat rate
9349
// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)
9350
int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)
9351
0
{
9352
0
    ImGuiContext& g = *GImGui;
9353
0
    const ImGuiKeyData* key_data = GetKeyData(key);
9354
0
    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
9355
0
        return 0;
9356
0
    const float t = key_data->DownDuration;
9357
0
    return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);
9358
0
}
9359
9360
// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).
9361
ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)
9362
0
{
9363
0
    return ImVec2(
9364
0
        GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,
9365
0
        GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);
9366
0
}
9367
9368
// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.
9369
//   Entries   D,A,B,B,A,C,B     --> A,A,B,B,B,C,D
9370
//   Index     A:1 B:2 C:5 D:0   --> A:0 B:2 C:5 D:6
9371
// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.
9372
static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)
9373
80.5k
{
9374
80.5k
    ImGuiContext& g = *GImGui;
9375
80.5k
    rt->EntriesNext.resize(0);
9376
12.5M
    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9377
12.4M
    {
9378
12.4M
        const int new_routing_start_idx = rt->EntriesNext.Size;
9379
12.4M
        ImGuiKeyRoutingData* routing_entry;
9380
12.4M
        for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)
9381
0
        {
9382
0
            routing_entry = &rt->Entries[old_routing_idx];
9383
0
            routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore;
9384
0
            routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry
9385
0
            routing_entry->RoutingNext = ImGuiKeyOwner_NoOwner;
9386
0
            routing_entry->RoutingNextScore = 0;
9387
0
            if (routing_entry->RoutingCurr == ImGuiKeyOwner_NoOwner)
9388
0
                continue;
9389
0
            rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer
9390
9391
            // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)
9392
            // This is the result of previous frame's SetShortcutRouting() call.
9393
0
            if (routing_entry->Mods == g.IO.KeyMods)
9394
0
            {
9395
0
                ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
9396
0
                if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
9397
0
                {
9398
0
                    owner_data->OwnerCurr = routing_entry->RoutingCurr;
9399
                    //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr);
9400
0
                }
9401
0
            }
9402
0
        }
9403
9404
        // Rewrite linked-list
9405
12.4M
        rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);
9406
12.4M
        for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)
9407
0
            rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);
9408
12.4M
    }
9409
80.5k
    rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes
9410
80.5k
}
9411
9412
// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().
9413
static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)
9414
0
{
9415
0
    ImGuiContext& g = *GImGui;
9416
0
    return (owner_id != ImGuiKeyOwner_NoOwner && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;
9417
0
}
9418
9419
ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)
9420
0
{
9421
    // Majority of shortcuts will be Key + any number of Mods
9422
    // We accept _Single_ mod with ImGuiKey_None.
9423
    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl);                    // Legal
9424
    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift);   // Legal
9425
    //  - Shortcut(ImGuiMod_Ctrl);                                 // Legal
9426
    //  - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift);                // Not legal
9427
0
    ImGuiContext& g = *GImGui;
9428
0
    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
9429
0
    ImGuiKeyRoutingData* routing_data;
9430
0
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9431
0
    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9432
0
    if (key == ImGuiKey_None)
9433
0
        key = ConvertSingleModFlagToKey(mods);
9434
0
    IM_ASSERT(IsNamedKey(key));
9435
9436
    // Get (in the majority of case, the linked list will have one element so this should be 2 reads.
9437
    // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).
9438
0
    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)
9439
0
    {
9440
0
        routing_data = &rt->Entries[idx];
9441
0
        if (routing_data->Mods == mods)
9442
0
            return routing_data;
9443
0
    }
9444
9445
    // Add to linked-list
9446
0
    ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;
9447
0
    rt->Entries.push_back(ImGuiKeyRoutingData());
9448
0
    routing_data = &rt->Entries[routing_data_idx];
9449
0
    routing_data->Mods = (ImU16)mods;
9450
0
    routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list
9451
0
    rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;
9452
0
    return routing_data;
9453
0
}
9454
9455
// Current score encoding
9456
//  -        0: Never route
9457
//  -        1: ImGuiInputFlags_RouteGlobal    (lower priority)
9458
//  - 100..199: ImGuiInputFlags_RouteFocused   (if window in focus-stack)
9459
//         200: ImGuiInputFlags_RouteGlobal  | ImGuiInputFlags_RouteOverFocused
9460
//         300: ImGuiInputFlags_RouteActive or ImGuiInputFlags_RouteFocused (if item active)
9461
//         400: ImGuiInputFlags_RouteGlobal  | ImGuiInputFlags_RouteOverActive
9462
//  - 500..599: ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteOverActive (if window in focus-stack) (higher priority)
9463
// 'flags' should include an explicit routing policy
9464
static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags)
9465
0
{
9466
0
    ImGuiContext& g = *GImGui;
9467
0
    if (flags & ImGuiInputFlags_RouteFocused)
9468
0
    {
9469
        // ActiveID gets high priority
9470
        // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)
9471
0
        if (owner_id != 0 && g.ActiveId == owner_id)
9472
0
            return 300;
9473
9474
        // Score based on distance to focused window (lower is better)
9475
        // Assuming both windows are submitting a routing request,
9476
        // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)
9477
        // - When Window/ChildB is focused -> Window scores 4,        Window/ChildB scores 3 (best)
9478
        // Assuming only WindowA is submitting a routing request,
9479
        // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.
9480
        // This essentially follow the window->ParentWindowForFocusRoute chain.
9481
0
        if (focus_scope_id == 0)
9482
0
            return 0;
9483
0
        for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++)
9484
0
            if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id)
9485
0
            {
9486
0
                if (flags & ImGuiInputFlags_RouteOverActive) // && g.ActiveId != 0 && g.ActiveId != owner_id)
9487
0
                    return 599 - index_in_focus_path;
9488
0
                else
9489
0
                    return 199 - index_in_focus_path;
9490
0
            }
9491
0
        return 0;
9492
0
    }
9493
0
    else if (flags & ImGuiInputFlags_RouteActive)
9494
0
    {
9495
0
        if (owner_id != 0 && g.ActiveId == owner_id)
9496
0
            return 300;
9497
0
        return 0;
9498
0
    }
9499
0
    else if (flags & ImGuiInputFlags_RouteGlobal)
9500
0
    {
9501
0
        if (flags & ImGuiInputFlags_RouteOverActive)
9502
0
            return 400;
9503
0
        if (owner_id != 0 && g.ActiveId == owner_id)
9504
0
            return 300;
9505
0
        if (flags & ImGuiInputFlags_RouteOverFocused)
9506
0
            return 200;
9507
0
        return 1;
9508
0
    }
9509
0
    IM_ASSERT(0);
9510
0
    return 0;
9511
0
}
9512
9513
// - We need this to filter some Shortcut() routes when an item e.g. an InputText() is active
9514
//   e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be.
9515
// - This is also used by UpdateInputEvents() to avoid trickling in the most common case of e.g. pressing ImGuiKey_G also emitting a G character.
9516
static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord)
9517
540
{
9518
    // Mimic 'ignore_char_inputs' logic in InputText()
9519
540
    ImGuiContext& g = *GImGui;
9520
9521
    // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out.
9522
540
    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
9523
540
    const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Ctrl));
9524
540
    if (ignore_char_inputs)
9525
0
        return false;
9526
9527
    // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered.
9528
540
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9529
540
    if (key == ImGuiKey_None)
9530
216
        return false;
9531
324
    return g.KeysMayBeCharInput.TestBit(key);
9532
540
}
9533
9534
// Request a desired route for an input chord (key + mods).
9535
// Return true if the route is available this frame.
9536
// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.
9537
//   (Conceptually this does a "Submit for next frame" + "Test for current frame".
9538
//   As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)
9539
bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
9540
161k
{
9541
161k
    ImGuiContext& g = *GImGui;
9542
161k
    if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
9543
0
        flags |= ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()
9544
161k
    else
9545
161k
        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteTypeMask_)); // Check that only 1 routing flag is used
9546
161k
    IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_NoOwner);
9547
161k
    if (flags & (ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteUnlessBgFocused))
9548
161k
        IM_ASSERT(flags & ImGuiInputFlags_RouteGlobal);
9549
161k
    if (flags & ImGuiInputFlags_RouteOverActive)
9550
161k
        IM_ASSERT(flags & (ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteFocused));
9551
9552
    // Add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified.
9553
161k
    key_chord = FixupKeyChord(key_chord);
9554
9555
    // [DEBUG] Debug break requested by user
9556
161k
    if (g.DebugBreakInShortcutRouting == key_chord)
9557
0
        IM_DEBUG_BREAK();
9558
9559
161k
    if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
9560
0
        if (g.NavWindow == NULL)
9561
0
            return false;
9562
9563
    // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this?
9564
161k
    if (flags & ImGuiInputFlags_RouteAlways)
9565
161k
    {
9566
161k
        IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> always, no register\n", GetKeyChordName(key_chord), flags, owner_id);
9567
161k
        return true;
9568
161k
    }
9569
9570
    // Specific culling when there's an active item.
9571
0
    if (g.ActiveId != 0 && g.ActiveId != owner_id)
9572
0
    {
9573
0
        if (flags & ImGuiInputFlags_RouteActive)
9574
0
            return false;
9575
9576
        // Cull shortcuts with no modifiers when it could generate a character.
9577
        // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active.
9578
        // but  Shortcut(Ctrl+G) should generally trigger when InputText() is active.
9579
        // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active.
9580
        // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined)
9581
0
        if (g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord))
9582
0
        {
9583
0
            IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> filtered as potential char input\n", GetKeyChordName(key_chord), flags, owner_id);
9584
0
            return false;
9585
0
        }
9586
9587
        // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId
9588
0
        if ((flags & ImGuiInputFlags_RouteOverActive) == 0 && g.ActiveIdUsingAllKeyboardKeys)
9589
0
        {
9590
0
            ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
9591
0
            if (key == ImGuiKey_None)
9592
0
                key = ConvertSingleModFlagToKey((ImGuiKey)(key_chord & ImGuiMod_Mask_));
9593
0
            if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
9594
0
                return false;
9595
0
        }
9596
0
    }
9597
9598
    // Where do we evaluate route for?
9599
0
    ImGuiID focus_scope_id = g.CurrentFocusScopeId;
9600
0
    if (flags & ImGuiInputFlags_RouteFromRootWindow)
9601
0
        focus_scope_id = g.CurrentWindow->RootWindow->ID; // See PushFocusScope() call in Begin()
9602
9603
0
    const int score = CalcRoutingScore(focus_scope_id, owner_id, flags);
9604
0
    IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, flags=%04X, owner_id=0x%08X) -> score %d\n", GetKeyChordName(key_chord), flags, owner_id, score);
9605
0
    if (score == 0)
9606
0
        return false;
9607
9608
    // Submit routing for NEXT frame (assuming score is sufficient)
9609
    // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using >= instead of >).
9610
0
    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);
9611
    //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score >= routing_data->RoutingNextScore) : (score > routing_data->RoutingNextScore);
9612
0
    if (score > routing_data->RoutingNextScore)
9613
0
    {
9614
0
        routing_data->RoutingNext = owner_id;
9615
0
        routing_data->RoutingNextScore = (ImU16)score;
9616
0
    }
9617
9618
    // Return routing state for CURRENT frame
9619
0
    if (routing_data->RoutingCurr == owner_id)
9620
0
        IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n");
9621
0
    return routing_data->RoutingCurr == owner_id;
9622
0
}
9623
9624
// Currently unused by core (but used by tests)
9625
// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.
9626
bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)
9627
0
{
9628
0
    const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);
9629
0
    key_chord = FixupKeyChord(key_chord);
9630
0
    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.
9631
0
    return routing_data->RoutingCurr == routing_id;
9632
0
}
9633
9634
// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.
9635
// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)
9636
bool ImGui::IsKeyDown(ImGuiKey key)
9637
1.21M
{
9638
1.21M
    return IsKeyDown(key, ImGuiKeyOwner_Any);
9639
1.21M
}
9640
9641
bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)
9642
1.21M
{
9643
1.21M
    const ImGuiKeyData* key_data = GetKeyData(key);
9644
1.21M
    if (!key_data->Down)
9645
1.08M
        return false;
9646
122k
    if (!TestKeyOwner(key, owner_id))
9647
0
        return false;
9648
122k
    return true;
9649
122k
}
9650
9651
bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)
9652
0
{
9653
0
    return IsKeyPressed(key, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9654
0
}
9655
9656
// Important: unlike legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.
9657
bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id)
9658
86.5k
{
9659
86.5k
    const ImGuiKeyData* key_data = GetKeyData(key);
9660
86.5k
    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
9661
86.5k
        return false;
9662
0
    const float t = key_data->DownDuration;
9663
0
    if (t < 0.0f)
9664
0
        return false;
9665
0
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!
9666
0
    if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat
9667
0
        flags |= ImGuiInputFlags_Repeat;
9668
9669
0
    bool pressed = (t == 0.0f);
9670
0
    if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0)
9671
0
    {
9672
0
        float repeat_delay, repeat_rate;
9673
0
        GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);
9674
0
        pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;
9675
0
        if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_))
9676
0
        {
9677
            // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value.
9678
            // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code.
9679
0
            ImGuiContext& g = *GImGui;
9680
0
            double key_pressed_time = g.Time - t + 0.00001f;
9681
0
            if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time))
9682
0
                pressed = false;
9683
0
            if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time))
9684
0
                pressed = false;
9685
0
            if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time))
9686
0
                pressed = false;
9687
0
        }
9688
0
    }
9689
0
    if (!pressed)
9690
0
        return false;
9691
0
    if (!TestKeyOwner(key, owner_id))
9692
0
        return false;
9693
0
    return true;
9694
0
}
9695
9696
bool ImGui::IsKeyReleased(ImGuiKey key)
9697
0
{
9698
0
    return IsKeyReleased(key, ImGuiKeyOwner_Any);
9699
0
}
9700
9701
bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)
9702
0
{
9703
0
    const ImGuiKeyData* key_data = GetKeyData(key);
9704
0
    if (key_data->DownDurationPrev < 0.0f || key_data->Down)
9705
0
        return false;
9706
0
    if (!TestKeyOwner(key, owner_id))
9707
0
        return false;
9708
0
    return true;
9709
0
}
9710
9711
bool ImGui::IsMouseDown(ImGuiMouseButton button)
9712
0
{
9713
0
    ImGuiContext& g = *GImGui;
9714
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9715
0
    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.
9716
0
}
9717
9718
bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)
9719
0
{
9720
0
    ImGuiContext& g = *GImGui;
9721
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9722
0
    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.
9723
0
}
9724
9725
bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)
9726
0
{
9727
0
    return IsMouseClicked(button, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None, ImGuiKeyOwner_Any);
9728
0
}
9729
9730
bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id)
9731
1.15k
{
9732
1.15k
    ImGuiContext& g = *GImGui;
9733
1.15k
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9734
1.15k
    if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)
9735
1.13k
        return false;
9736
16
    const float t = g.IO.MouseDownDuration[button];
9737
16
    if (t < 0.0f)
9738
0
        return false;
9739
16
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here.
9740
9741
16
    const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;
9742
16
    const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);
9743
16
    if (!pressed)
9744
14
        return false;
9745
9746
2
    if (!TestKeyOwner(MouseButtonToKey(button), owner_id))
9747
0
        return false;
9748
9749
2
    return true;
9750
2
}
9751
9752
bool ImGui::IsMouseReleased(ImGuiMouseButton button)
9753
0
{
9754
0
    ImGuiContext& g = *GImGui;
9755
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9756
0
    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)
9757
0
}
9758
9759
bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)
9760
1.15k
{
9761
1.15k
    ImGuiContext& g = *GImGui;
9762
1.15k
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9763
1.15k
    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)
9764
1.15k
}
9765
9766
// Use if you absolutely need to distinguish single-click from double-click by introducing a delay.
9767
// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test.
9768
// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
9769
bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay)
9770
0
{
9771
0
    ImGuiContext& g = *GImGui;
9772
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9773
0
    const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]);
9774
0
    return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay);
9775
0
}
9776
9777
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)
9778
0
{
9779
0
    ImGuiContext& g = *GImGui;
9780
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9781
0
    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);
9782
0
}
9783
9784
bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)
9785
0
{
9786
0
    ImGuiContext& g = *GImGui;
9787
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9788
0
    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);
9789
0
}
9790
9791
int ImGui::GetMouseClickedCount(ImGuiMouseButton button)
9792
0
{
9793
0
    ImGuiContext& g = *GImGui;
9794
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9795
0
    return g.IO.MouseClickedCount[button];
9796
0
}
9797
9798
// Test if mouse cursor is hovering given rectangle
9799
// NB- Rectangle is clipped by our current clip setting
9800
// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)
9801
bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)
9802
489k
{
9803
489k
    ImGuiContext& g = *GImGui;
9804
9805
    // Clip
9806
489k
    ImRect rect_clipped(r_min, r_max);
9807
489k
    if (clip)
9808
327k
        rect_clipped.ClipWith(g.CurrentWindow->ClipRect);
9809
9810
    // Hit testing, expanded for touch input
9811
489k
    if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))
9812
487k
        return false;
9813
2.30k
    return true;
9814
489k
}
9815
9816
// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.
9817
// [Internal] This doesn't test if the button is pressed
9818
bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)
9819
16
{
9820
16
    ImGuiContext& g = *GImGui;
9821
16
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9822
16
    if (lock_threshold < 0.0f)
9823
0
        lock_threshold = g.IO.MouseDragThreshold;
9824
16
    return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;
9825
16
}
9826
9827
bool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)
9828
402k
{
9829
402k
    ImGuiContext& g = *GImGui;
9830
402k
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9831
402k
    if (!g.IO.MouseDown[button])
9832
402k
        return false;
9833
16
    return IsMouseDragPastThreshold(button, lock_threshold);
9834
402k
}
9835
9836
ImVec2 ImGui::GetMousePos()
9837
0
{
9838
0
    ImGuiContext& g = *GImGui;
9839
0
    return g.IO.MousePos;
9840
0
}
9841
9842
// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.
9843
// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.
9844
void ImGui::TeleportMousePos(const ImVec2& pos)
9845
0
{
9846
0
    ImGuiContext& g = *GImGui;
9847
0
    g.IO.MousePos = g.IO.MousePosPrev = pos;
9848
0
    g.IO.MouseDelta = ImVec2(0.0f, 0.0f);
9849
0
    g.IO.WantSetMousePos = true;
9850
    //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y);
9851
0
}
9852
9853
// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!
9854
ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()
9855
0
{
9856
0
    ImGuiContext& g = *GImGui;
9857
0
    if (g.BeginPopupStack.Size > 0)
9858
0
        return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;
9859
0
    return g.IO.MousePos;
9860
0
}
9861
9862
// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.
9863
bool ImGui::IsMousePosValid(const ImVec2* mouse_pos)
9864
241k
{
9865
    // The assert is only to silence a false-positive in XCode Static Analysis.
9866
    // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).
9867
241k
    IM_ASSERT(GImGui != NULL);
9868
241k
    const float MOUSE_INVALID = -256000.0f;
9869
241k
    ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;
9870
241k
    return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;
9871
241k
}
9872
9873
// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
9874
bool ImGui::IsAnyMouseDown()
9875
0
{
9876
0
    ImGuiContext& g = *GImGui;
9877
0
    for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)
9878
0
        if (g.IO.MouseDown[n])
9879
0
            return true;
9880
0
    return false;
9881
0
}
9882
9883
// Return the delta from the initial clicking position while the mouse button is clicked or was just released.
9884
// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.
9885
// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.
9886
ImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)
9887
0
{
9888
0
    ImGuiContext& g = *GImGui;
9889
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9890
0
    if (lock_threshold < 0.0f)
9891
0
        lock_threshold = g.IO.MouseDragThreshold;
9892
0
    if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])
9893
0
        if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)
9894
0
            if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))
9895
0
                return g.IO.MousePos - g.IO.MouseClickedPos[button];
9896
0
    return ImVec2(0.0f, 0.0f);
9897
0
}
9898
9899
void ImGui::ResetMouseDragDelta(ImGuiMouseButton button)
9900
0
{
9901
0
    ImGuiContext& g = *GImGui;
9902
0
    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));
9903
    // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr
9904
0
    g.IO.MouseClickedPos[button] = g.IO.MousePos;
9905
0
}
9906
9907
// Get desired mouse cursor shape.
9908
// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),
9909
// updated during the frame, and locked in EndFrame()/Render().
9910
// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you
9911
ImGuiMouseCursor ImGui::GetMouseCursor()
9912
80.5k
{
9913
80.5k
    ImGuiContext& g = *GImGui;
9914
80.5k
    return g.MouseCursor;
9915
80.5k
}
9916
9917
// We intentionally accept values of ImGuiMouseCursor that are outside our bounds, in case users needs to hack-in a custom cursor value.
9918
// Custom cursors may be handled by custom backends. If you are using a standard backend and want to hack in a custom cursor, you may
9919
// handle it before the backend _NewFrame() call and temporarily set ImGuiConfigFlags_NoMouseCursorChange during the backend _NewFrame() call.
9920
void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)
9921
0
{
9922
0
    ImGuiContext& g = *GImGui;
9923
0
    g.MouseCursor = cursor_type;
9924
0
}
9925
9926
static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)
9927
564k
{
9928
564k
    IM_ASSERT(ImGui::IsAliasKey(key));
9929
564k
    ImGuiKeyData* key_data = ImGui::GetKeyData(key);
9930
564k
    key_data->Down = v;
9931
564k
    key_data->AnalogValue = analog_value;
9932
564k
}
9933
9934
// [Internal] Do not use directly
9935
static ImGuiKeyChord GetMergedModsFromKeys()
9936
161k
{
9937
161k
    ImGuiKeyChord mods = 0;
9938
161k
    if (ImGui::IsKeyDown(ImGuiMod_Ctrl))     { mods |= ImGuiMod_Ctrl; }
9939
161k
    if (ImGui::IsKeyDown(ImGuiMod_Shift))    { mods |= ImGuiMod_Shift; }
9940
161k
    if (ImGui::IsKeyDown(ImGuiMod_Alt))      { mods |= ImGuiMod_Alt; }
9941
161k
    if (ImGui::IsKeyDown(ImGuiMod_Super))    { mods |= ImGuiMod_Super; }
9942
161k
    return mods;
9943
161k
}
9944
9945
static void ImGui::UpdateKeyboardInputs()
9946
80.5k
{
9947
80.5k
    ImGuiContext& g = *GImGui;
9948
80.5k
    ImGuiIO& io = g.IO;
9949
9950
80.5k
    if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
9951
0
        io.ClearInputKeys();
9952
9953
    // Update aliases
9954
483k
    for (int n = 0; n < ImGuiMouseButton_COUNT; n++)
9955
402k
        UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);
9956
80.5k
    UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);
9957
80.5k
    UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);
9958
9959
    // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values.
9960
    // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) ->                                      -> (here) deriving io.KeyMods + io.KeyXXX from key array.
9961
    // - Legacy backends:      set io.KeyXXX bools               -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.
9962
    // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.
9963
80.5k
    const ImGuiKeyChord prev_key_mods = io.KeyMods;
9964
80.5k
    io.KeyMods = GetMergedModsFromKeys();
9965
80.5k
    io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;
9966
80.5k
    io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;
9967
80.5k
    io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;
9968
80.5k
    io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;
9969
80.5k
    if (prev_key_mods != io.KeyMods)
9970
216
        g.LastKeyModsChangeTime = g.Time;
9971
80.5k
    if (prev_key_mods != io.KeyMods && prev_key_mods == 0)
9972
108
        g.LastKeyModsChangeFromNoneTime = g.Time;
9973
9974
    // Clear gamepad data if disabled
9975
80.5k
    if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)
9976
2.01M
        for (int key = ImGuiKey_Gamepad_BEGIN; key < ImGuiKey_Gamepad_END; key++)
9977
1.93M
        {
9978
1.93M
            io.KeysData[key - ImGuiKey_NamedKey_BEGIN].Down = false;
9979
1.93M
            io.KeysData[key - ImGuiKey_NamedKey_BEGIN].AnalogValue = 0.0f;
9980
1.93M
        }
9981
9982
    // Update keys
9983
12.5M
    for (int key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key++)
9984
12.4M
    {
9985
12.4M
        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
9986
12.4M
        key_data->DownDurationPrev = key_data->DownDuration;
9987
12.4M
        key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;
9988
12.4M
        if (key_data->DownDuration == 0.0f)
9989
272
        {
9990
272
            if (IsKeyboardKey((ImGuiKey)key))
9991
162
                g.LastKeyboardKeyPressTime = g.Time;
9992
110
            else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper)
9993
108
                g.LastKeyboardKeyPressTime = g.Time;
9994
272
        }
9995
12.4M
    }
9996
9997
    // Update keys/input owner (named keys only): one entry per key
9998
12.5M
    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
9999
12.4M
    {
10000
12.4M
        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_NamedKey_BEGIN];
10001
12.4M
        ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];
10002
12.4M
        owner_data->OwnerCurr = owner_data->OwnerNext;
10003
12.4M
        if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.
10004
12.3M
            owner_data->OwnerNext = ImGuiKeyOwner_NoOwner;
10005
12.4M
        owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down;  // Clear LockUntilRelease when key is not Down anymore
10006
12.4M
    }
10007
10008
    // Update key routing (for e.g. shortcuts)
10009
80.5k
    UpdateKeyRoutingTable(&g.KeysRoutingTable);
10010
80.5k
}
10011
10012
static void ImGui::UpdateMouseInputs()
10013
80.5k
{
10014
80.5k
    ImGuiContext& g = *GImGui;
10015
80.5k
    ImGuiIO& io = g.IO;
10016
10017
    // Mouse Wheel swapping flag
10018
    // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead
10019
    // - We avoid doing it on OSX as it the OS input layer handles this already.
10020
    // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.
10021
    // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.
10022
80.5k
    io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;
10023
10024
    // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)
10025
80.5k
    if (IsMousePosValid(&io.MousePos))
10026
80.5k
        io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);
10027
10028
    // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta
10029
80.5k
    if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))
10030
80.5k
        io.MouseDelta = io.MousePos - io.MousePosPrev;
10031
2
    else
10032
2
        io.MouseDelta = ImVec2(0.0f, 0.0f);
10033
10034
    // Update stationary timer.
10035
    // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.
10036
80.5k
    const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
10037
80.5k
    const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);
10038
80.5k
    g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;
10039
    //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer);
10040
10041
    // If mouse moved we re-enable mouse hovering in case it was disabled by keyboard/gamepad. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
10042
80.5k
    if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
10043
148
        g.NavHighlightItemUnderNav = false;
10044
10045
483k
    for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)
10046
402k
    {
10047
402k
        io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;
10048
402k
        io.MouseClickedCount[i] = 0; // Will be filled below
10049
402k
        io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;
10050
402k
        if (io.MouseReleased[i])
10051
2
            io.MouseReleasedTime[i] = g.Time;
10052
402k
        io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];
10053
402k
        io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;
10054
402k
        if (io.MouseClicked[i])
10055
2
        {
10056
2
            bool is_repeated_click = false;
10057
2
            if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)
10058
0
            {
10059
0
                ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);
10060
0
                if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)
10061
0
                    is_repeated_click = true;
10062
0
            }
10063
2
            if (is_repeated_click)
10064
0
                io.MouseClickedLastCount[i]++;
10065
2
            else
10066
2
                io.MouseClickedLastCount[i] = 1;
10067
2
            io.MouseClickedTime[i] = g.Time;
10068
2
            io.MouseClickedPos[i] = io.MousePos;
10069
2
            io.MouseClickedCount[i] = io.MouseClickedLastCount[i];
10070
2
            io.MouseDragMaxDistanceSqr[i] = 0.0f;
10071
2
        }
10072
402k
        else if (io.MouseDown[i])
10073
14
        {
10074
            // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold
10075
14
            float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f;
10076
14
            io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos);
10077
14
        }
10078
10079
        // We provide io.MouseDoubleClicked[] as a legacy service
10080
402k
        io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);
10081
10082
        // Clicking any mouse button reactivate mouse hovering which may have been deactivated by keyboard/gamepad navigation
10083
402k
        if (io.MouseClicked[i])
10084
2
            g.NavHighlightItemUnderNav = false;
10085
402k
    }
10086
80.5k
}
10087
10088
static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)
10089
0
{
10090
0
    ImGuiContext& g = *GImGui;
10091
0
    if (window)
10092
0
        g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);
10093
0
    else
10094
0
        g.WheelingWindowReleaseTimer = 0.0f;
10095
0
    if (g.WheelingWindow == window)
10096
0
        return;
10097
0
    IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL");
10098
0
    g.WheelingWindow = window;
10099
0
    g.WheelingWindowRefMousePos = g.IO.MousePos;
10100
0
    if (window == NULL)
10101
0
    {
10102
0
        g.WheelingWindowStartFrame = -1;
10103
0
        g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);
10104
0
    }
10105
0
}
10106
10107
static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)
10108
0
{
10109
    // For each axis, find window in the hierarchy that may want to use scrolling
10110
0
    ImGuiContext& g = *GImGui;
10111
0
    ImGuiWindow* windows[2] = { NULL, NULL };
10112
0
    for (int axis = 0; axis < 2; axis++)
10113
0
        if (wheel[axis] != 0.0f)
10114
0
            for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)
10115
0
            {
10116
                // Bubble up into parent window if:
10117
                // - a child window doesn't allow any scrolling.
10118
                // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.
10119
                //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)
10120
0
                const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);
10121
0
                const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);
10122
                //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);
10123
0
                if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)
10124
0
                    break; // select this window
10125
0
            }
10126
0
    if (windows[0] == NULL && windows[1] == NULL)
10127
0
        return NULL;
10128
10129
    // If there's only one window or only one axis then there's no ambiguity
10130
0
    if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)
10131
0
        return windows[1] ? windows[1] : windows[0];
10132
10133
    // If candidate are different windows we need to decide which one to prioritize
10134
    // - First frame: only find a winner if one axis is zero.
10135
    // - Subsequent frames: only find a winner when one is more than the other.
10136
0
    if (g.WheelingWindowStartFrame == -1)
10137
0
        g.WheelingWindowStartFrame = g.FrameCount;
10138
0
    if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))
10139
0
    {
10140
0
        g.WheelingWindowWheelRemainder = wheel;
10141
0
        return NULL;
10142
0
    }
10143
0
    return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];
10144
0
}
10145
10146
// Called by NewFrame()
10147
void ImGui::UpdateMouseWheel()
10148
80.5k
{
10149
    // Reset the locked window if we move the mouse or after the timer elapses.
10150
    // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795)
10151
80.5k
    ImGuiContext& g = *GImGui;
10152
80.5k
    if (g.WheelingWindow != NULL)
10153
0
    {
10154
0
        g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;
10155
0
        if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)
10156
0
            g.WheelingWindowReleaseTimer = 0.0f;
10157
0
        if (g.WheelingWindowReleaseTimer <= 0.0f)
10158
0
            LockWheelingWindow(NULL, 0.0f);
10159
0
    }
10160
10161
80.5k
    ImVec2 wheel;
10162
80.5k
    wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheelH : 0.0f;
10163
80.5k
    wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_NoOwner) ? g.IO.MouseWheel : 0.0f;
10164
10165
    //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y);
10166
80.5k
    ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;
10167
80.5k
    if (!mouse_window || mouse_window->Collapsed)
10168
79.3k
        return;
10169
10170
    // Zoom / Scale window
10171
    // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.
10172
1.19k
    if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)
10173
0
    {
10174
0
        LockWheelingWindow(mouse_window, wheel.y);
10175
0
        ImGuiWindow* window = mouse_window;
10176
0
        const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);
10177
0
        const float scale = new_font_scale / window->FontWindowScale;
10178
0
        window->FontWindowScale = new_font_scale;
10179
0
        if (window == window->RootWindow)
10180
0
        {
10181
0
            const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;
10182
0
            SetWindowPos(window, window->Pos + offset, 0);
10183
0
            window->Size = ImTrunc(window->Size * scale);
10184
0
            window->SizeFull = ImTrunc(window->SizeFull * scale);
10185
0
        }
10186
0
        return;
10187
0
    }
10188
1.19k
    if (g.IO.KeyCtrl)
10189
0
        return;
10190
10191
    // Mouse wheel scrolling
10192
    // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()
10193
1.19k
    if (g.IO.MouseWheelRequestAxisSwap)
10194
0
        wheel = ImVec2(wheel.y, 0.0f);
10195
10196
    // Maintain a rough average of moving magnitude on both axes
10197
    // FIXME: should by based on wall clock time rather than frame-counter
10198
1.19k
    g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);
10199
1.19k
    g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);
10200
10201
    // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.
10202
1.19k
    wheel += g.WheelingWindowWheelRemainder;
10203
1.19k
    g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);
10204
1.19k
    if (wheel.x == 0.0f && wheel.y == 0.0f)
10205
1.19k
        return;
10206
10207
    // Mouse wheel scrolling: find target and apply
10208
    // - don't renew lock if axis doesn't apply on the window.
10209
    // - select a main axis when both axes are being moved.
10210
0
    if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))
10211
0
        if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))
10212
0
        {
10213
0
            bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };
10214
0
            if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])
10215
0
                do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;
10216
0
            if (do_scroll[ImGuiAxis_X])
10217
0
            {
10218
0
                LockWheelingWindow(window, wheel.x);
10219
0
                float max_step = window->InnerRect.GetWidth() * 0.67f;
10220
0
                float scroll_step = ImTrunc(ImMin(2 * window->FontRefSize, max_step));
10221
0
                SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
10222
0
                g.WheelingWindowScrolledFrame = g.FrameCount;
10223
0
            }
10224
0
            if (do_scroll[ImGuiAxis_Y])
10225
0
            {
10226
0
                LockWheelingWindow(window, wheel.y);
10227
0
                float max_step = window->InnerRect.GetHeight() * 0.67f;
10228
0
                float scroll_step = ImTrunc(ImMin(5 * window->FontRefSize, max_step));
10229
0
                SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
10230
0
                g.WheelingWindowScrolledFrame = g.FrameCount;
10231
0
            }
10232
0
        }
10233
0
}
10234
10235
void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)
10236
0
{
10237
0
    ImGuiContext& g = *GImGui;
10238
0
    g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;
10239
0
}
10240
10241
void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)
10242
0
{
10243
0
    ImGuiContext& g = *GImGui;
10244
0
    g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;
10245
0
}
10246
10247
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10248
static const char* GetInputSourceName(ImGuiInputSource source)
10249
0
{
10250
0
    const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad" };
10251
0
    IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
10252
0
    if (source < 0 || source >= ImGuiInputSource_COUNT)
10253
0
        return "Unknown";
10254
0
    return input_source_names[source];
10255
0
}
10256
static const char* GetMouseSourceName(ImGuiMouseSource source)
10257
0
{
10258
0
    const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" };
10259
0
    IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT);
10260
0
    if (source < 0 || source >= ImGuiMouseSource_COUNT)
10261
0
        return "Unknown";
10262
0
    return mouse_source_names[source];
10263
0
}
10264
static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)
10265
0
{
10266
0
    ImGuiContext& g = *GImGui;
10267
0
    if (e->Type == ImGuiInputEventType_MousePos)    { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }
10268
0
    if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; }
10269
0
    if (e->Type == ImGuiInputEventType_MouseWheel)  { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }
10270
0
    if (e->Type == ImGuiInputEventType_Key)         { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; }
10271
0
    if (e->Type == ImGuiInputEventType_Text)        { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; }
10272
0
    if (e->Type == ImGuiInputEventType_Focus)       { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; }
10273
0
}
10274
#endif
10275
10276
// Process input queue
10277
// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.
10278
// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)
10279
// - trickle_fast_inputs = true  : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)
10280
void ImGui::UpdateInputEvents(bool trickle_fast_inputs)
10281
80.5k
{
10282
80.5k
    ImGuiContext& g = *GImGui;
10283
80.5k
    ImGuiIO& io = g.IO;
10284
10285
    // Only trickle chars<>key when working with InputText()
10286
    // FIXME: InputText() could parse event trail?
10287
    // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)
10288
80.5k
    const bool trickle_interleaved_nonchar_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);
10289
10290
80.5k
    bool mouse_moved = false, mouse_wheeled = false, key_changed = false, key_changed_nonchar = false, text_inputted = false;
10291
80.5k
    int  mouse_button_changed = 0x00;
10292
80.5k
    ImBitArray<ImGuiKey_NamedKey_COUNT> key_changed_mask;
10293
10294
80.5k
    int event_n = 0;
10295
81.2k
    for (; event_n < g.InputEventsQueue.Size; event_n++)
10296
699
    {
10297
699
        ImGuiInputEvent* e = &g.InputEventsQueue[event_n];
10298
699
        if (e->Type == ImGuiInputEventType_MousePos)
10299
154
        {
10300
154
            if (g.IO.WantSetMousePos)
10301
0
                continue;
10302
            // Trickling Rule: Stop processing queued events if we already handled a mouse button change
10303
154
            ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);
10304
154
            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))
10305
0
                break;
10306
154
            io.MousePos = event_pos;
10307
154
            io.MouseSource = e->MousePos.MouseSource;
10308
154
            mouse_moved = true;
10309
154
        }
10310
545
        else if (e->Type == ImGuiInputEventType_MouseButton)
10311
5
        {
10312
            // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10313
5
            const ImGuiMouseButton button = e->MouseButton.Button;
10314
5
            IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);
10315
5
            if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))
10316
1
                break;
10317
4
            if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.
10318
0
                break;
10319
4
            io.MouseDown[button] = e->MouseButton.Down;
10320
4
            io.MouseSource = e->MouseButton.MouseSource;
10321
4
            mouse_button_changed |= (1 << button);
10322
4
        }
10323
540
        else if (e->Type == ImGuiInputEventType_MouseWheel)
10324
0
        {
10325
            // Trickling Rule: Stop processing queued events if we got multiple action on the event
10326
0
            if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))
10327
0
                break;
10328
0
            io.MouseWheelH += e->MouseWheel.WheelX;
10329
0
            io.MouseWheel += e->MouseWheel.WheelY;
10330
0
            io.MouseSource = e->MouseWheel.MouseSource;
10331
0
            mouse_wheeled = true;
10332
0
        }
10333
540
        else if (e->Type == ImGuiInputEventType_Key)
10334
540
        {
10335
            // Trickling Rule: Stop processing queued events if we got multiple action on the same button
10336
540
            if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10337
0
                continue;
10338
540
            ImGuiKey key = e->Key.Key;
10339
540
            IM_ASSERT(key != ImGuiKey_None);
10340
540
            ImGuiKeyData* key_data = GetKeyData(key);
10341
540
            const int key_data_index = (int)(key_data - g.IO.KeysData);
10342
540
            if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || mouse_button_changed != 0))
10343
0
                break;
10344
10345
540
            const bool key_is_potentially_for_char_input = IsKeyChordPotentiallyCharInput(GetMergedModsFromKeys() | key);
10346
540
            if (trickle_interleaved_nonchar_keys_and_text && (text_inputted && !key_is_potentially_for_char_input))
10347
0
                break;
10348
10349
540
            if (key_data->Down != e->Key.Down) // Analog change only do not trigger this, so it won't block e.g. further mouse pos events testing key_changed.
10350
540
            {
10351
540
                key_changed = true;
10352
540
                key_changed_mask.SetBit(key_data_index);
10353
540
                if (trickle_interleaved_nonchar_keys_and_text && !key_is_potentially_for_char_input)
10354
0
                    key_changed_nonchar = true;
10355
540
            }
10356
10357
540
            key_data->Down = e->Key.Down;
10358
540
            key_data->AnalogValue = e->Key.AnalogValue;
10359
540
        }
10360
0
        else if (e->Type == ImGuiInputEventType_Text)
10361
0
        {
10362
0
            if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)
10363
0
                continue;
10364
            // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with
10365
0
            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_moved || mouse_wheeled))
10366
0
                break;
10367
0
            if (trickle_interleaved_nonchar_keys_and_text && key_changed_nonchar)
10368
0
                break;
10369
0
            unsigned int c = e->Text.Char;
10370
0
            io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);
10371
0
            if (trickle_interleaved_nonchar_keys_and_text)
10372
0
                text_inputted = true;
10373
0
        }
10374
0
        else if (e->Type == ImGuiInputEventType_Focus)
10375
0
        {
10376
            // We intentionally overwrite this and process in NewFrame(), in order to give a chance
10377
            // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.
10378
0
            const bool focus_lost = !e->AppFocused.Focused;
10379
0
            io.AppFocusLost = focus_lost;
10380
0
        }
10381
0
        else
10382
0
        {
10383
0
            IM_ASSERT(0 && "Unknown event!");
10384
0
        }
10385
699
    }
10386
10387
    // Record trail (for domain-specific applications wanting to access a precise trail)
10388
    //if (event_n != 0) IMGUI_DEBUG_LOG_IO("Processed: %d / Remaining: %d\n", event_n, g.InputEventsQueue.Size - event_n);
10389
81.2k
    for (int n = 0; n < event_n; n++)
10390
698
        g.InputEventsTrail.push_back(g.InputEventsQueue[n]);
10391
10392
    // [DEBUG]
10393
80.5k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10394
80.5k
    if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))
10395
0
        for (int n = 0; n < g.InputEventsQueue.Size; n++)
10396
0
            DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);
10397
80.5k
#endif
10398
10399
    // Remaining events will be processed on the next frame
10400
80.5k
    if (event_n == g.InputEventsQueue.Size)
10401
80.5k
        g.InputEventsQueue.resize(0);
10402
1
    else
10403
1
        g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);
10404
10405
    // Clear buttons state when focus is lost
10406
    // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.
10407
    // - we clear in EndFrame() and not now in order allow application/user code polling this flag
10408
    //   (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event).
10409
80.5k
    if (g.IO.AppFocusLost)
10410
0
    {
10411
0
        g.IO.ClearInputKeys();
10412
0
        g.IO.ClearInputMouse();
10413
0
    }
10414
80.5k
}
10415
10416
ImGuiID ImGui::GetKeyOwner(ImGuiKey key)
10417
0
{
10418
0
    if (!IsNamedKeyOrMod(key))
10419
0
        return ImGuiKeyOwner_NoOwner;
10420
10421
0
    ImGuiContext& g = *GImGui;
10422
0
    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10423
0
    ImGuiID owner_id = owner_data->OwnerCurr;
10424
10425
0
    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10426
0
        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10427
0
            return ImGuiKeyOwner_NoOwner;
10428
10429
0
    return owner_id;
10430
0
}
10431
10432
// TestKeyOwner(..., ID)   : (owner == None || owner == ID)
10433
// TestKeyOwner(..., None) : (owner == None)
10434
// TestKeyOwner(..., Any)  : no owner test
10435
// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.
10436
bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)
10437
283k
{
10438
283k
    if (!IsNamedKeyOrMod(key))
10439
0
        return true;
10440
10441
283k
    ImGuiContext& g = *GImGui;
10442
283k
    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)
10443
0
        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)
10444
0
            return false;
10445
10446
283k
    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10447
283k
    if (owner_id == ImGuiKeyOwner_Any)
10448
122k
        return (owner_data->LockThisFrame == false);
10449
10450
    // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId
10451
    // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.
10452
    // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.
10453
161k
    if (owner_data->OwnerCurr != owner_id)
10454
4
    {
10455
4
        if (owner_data->LockThisFrame)
10456
0
            return false;
10457
4
        if (owner_data->OwnerCurr != ImGuiKeyOwner_NoOwner)
10458
0
            return false;
10459
4
    }
10460
10461
161k
    return true;
10462
161k
}
10463
10464
// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.
10465
// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.
10466
// - SetKeyOwner(..., None)              : clears owner
10467
// - SetKeyOwner(..., Any, !Lock)        : illegal (assert)
10468
// - SetKeyOwner(..., Any or None, Lock) : set lock
10469
void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)
10470
0
{
10471
0
    ImGuiContext& g = *GImGui;
10472
0
    IM_ASSERT(IsNamedKeyOrMod(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)
10473
0
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!
10474
    //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags);
10475
10476
0
    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
10477
0
    owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;
10478
10479
    // We cannot lock by default as it would likely break lots of legacy code.
10480
    // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)
10481
0
    owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;
10482
0
    owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);
10483
0
}
10484
10485
// Rarely used helper
10486
void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)
10487
0
{
10488
0
    if (key_chord & ImGuiMod_Ctrl)      { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }
10489
0
    if (key_chord & ImGuiMod_Shift)     { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }
10490
0
    if (key_chord & ImGuiMod_Alt)       { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }
10491
0
    if (key_chord & ImGuiMod_Super)     { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }
10492
0
    if (key_chord & ~ImGuiMod_Mask_)    { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }
10493
0
}
10494
10495
// This is more or less equivalent to:
10496
//   if (IsItemHovered() || IsItemActive())
10497
//       SetKeyOwner(key, GetItemID());
10498
// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.
10499
// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.
10500
// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.
10501
void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)
10502
0
{
10503
0
    ImGuiContext& g = *GImGui;
10504
0
    ImGuiID id = g.LastItemData.ID;
10505
0
    if (id == 0 || (g.HoveredId != id && g.ActiveId != id))
10506
0
        return;
10507
0
    if ((flags & ImGuiInputFlags_CondMask_) == 0)
10508
0
        flags |= ImGuiInputFlags_CondDefault_;
10509
0
    if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))
10510
0
    {
10511
0
        IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!
10512
0
        SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);
10513
0
    }
10514
0
}
10515
10516
void ImGui::SetItemKeyOwner(ImGuiKey key)
10517
0
{
10518
0
    SetItemKeyOwner(key, ImGuiInputFlags_None);
10519
0
}
10520
10521
// This is the only public API until we expose owner_id versions of the API as replacements.
10522
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)
10523
0
{
10524
0
    return IsKeyChordPressed(key_chord, ImGuiInputFlags_None, ImGuiKeyOwner_Any);
10525
0
}
10526
10527
// This is equivalent to comparing KeyMods + doing a IsKeyPressed()
10528
bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10529
161k
{
10530
161k
    ImGuiContext& g = *GImGui;
10531
161k
    key_chord = FixupKeyChord(key_chord);
10532
161k
    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);
10533
161k
    if (g.IO.KeyMods != mods)
10534
161k
        return false;
10535
10536
    // Special storage location for mods
10537
0
    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);
10538
0
    if (key == ImGuiKey_None)
10539
0
        key = ConvertSingleModFlagToKey(mods);
10540
0
    if (!IsKeyPressed(key, (flags & ImGuiInputFlags_RepeatMask_), owner_id))
10541
0
        return false;
10542
0
    return true;
10543
0
}
10544
10545
void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10546
0
{
10547
0
    ImGuiContext& g = *GImGui;
10548
0
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasShortcut;
10549
0
    g.NextItemData.Shortcut = key_chord;
10550
0
    g.NextItemData.ShortcutFlags = flags;
10551
0
}
10552
10553
// Called from within ItemAdd: at this point we can read from NextItemData and write to LastItemData
10554
void ImGui::ItemHandleShortcut(ImGuiID id)
10555
0
{
10556
0
    ImGuiContext& g = *GImGui;
10557
0
    ImGuiInputFlags flags = g.NextItemData.ShortcutFlags;
10558
0
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetNextItemShortcut) == 0); // Passing flags not supported by SetNextItemShortcut()!
10559
10560
0
    if (g.LastItemData.ItemFlags & ImGuiItemFlags_Disabled)
10561
0
        return;
10562
0
    if (flags & ImGuiInputFlags_Tooltip)
10563
0
    {
10564
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasShortcut;
10565
0
        g.LastItemData.Shortcut = g.NextItemData.Shortcut;
10566
0
    }
10567
0
    if (!Shortcut(g.NextItemData.Shortcut, flags & ImGuiInputFlags_SupportedByShortcut, id) || g.NavActivateId != 0)
10568
0
        return;
10569
10570
    // FIXME: Generalize Activation queue?
10571
0
    g.NavActivateId = id; // Will effectively disable clipping.
10572
0
    g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut;
10573
    //if (g.ActiveId == 0 || g.ActiveId == id)
10574
0
    g.NavActivateDownId = g.NavActivatePressedId = id;
10575
0
    NavHighlightActivated(id);
10576
0
}
10577
10578
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags)
10579
0
{
10580
0
    return Shortcut(key_chord, flags, ImGuiKeyOwner_Any);
10581
0
}
10582
10583
bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id)
10584
161k
{
10585
161k
    ImGuiContext& g = *GImGui;
10586
    //IMGUI_DEBUG_LOG("Shortcut(%s, flags=%X, owner_id=0x%08X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), flags, owner_id);
10587
10588
    // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.
10589
161k
    if ((flags & ImGuiInputFlags_RouteTypeMask_) == 0)
10590
0
        flags |= ImGuiInputFlags_RouteFocused;
10591
10592
    // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)
10593
    // Effectively makes Shortcut() always input-owner aware.
10594
161k
    if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_NoOwner)
10595
0
        owner_id = GetRoutingIdFromOwnerId(owner_id);
10596
10597
161k
    if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10598
0
        return false;
10599
10600
    // Submit route
10601
161k
    if (!SetShortcutRouting(key_chord, flags, owner_id))
10602
0
        return false;
10603
10604
    // Default repeat behavior for Shortcut()
10605
    // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut.
10606
161k
    if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0)
10607
161k
        flags |= ImGuiInputFlags_RepeatUntilKeyModsChange;
10608
10609
161k
    if (!IsKeyChordPressed(key_chord, flags, owner_id))
10610
161k
        return false;
10611
10612
    // Claim mods during the press
10613
0
    SetKeyOwnersForKeyChord(key_chord & ImGuiMod_Mask_, owner_id);
10614
10615
0
    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!
10616
0
    return true;
10617
0
}
10618
10619
//-----------------------------------------------------------------------------
10620
// [SECTION] ERROR CHECKING, STATE RECOVERY
10621
//-----------------------------------------------------------------------------
10622
// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros)
10623
// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10624
// - ErrorCheckNewFrameSanityChecks()
10625
// - ErrorCheckEndFrameSanityChecks()
10626
// - ErrorRecoveryStoreState()
10627
// - ErrorRecoveryTryToRecoverState()
10628
// - ErrorRecoveryTryToRecoverWindowState()
10629
// - ErrorLog()
10630
//-----------------------------------------------------------------------------
10631
10632
// Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
10633
// Called by IMGUI_CHECKVERSION().
10634
// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit
10635
// If this triggers you have mismatched headers and compiled code versions.
10636
// - It could be because of a build issue (using new headers with old compiled code)
10637
// - It could be because of mismatched configuration #define, compilation settings, packing pragma etc.
10638
//   THE CONFIGURATION SETTINGS MENTIONED IN imconfig.h MUST BE SET FOR ALL COMPILATION UNITS INVOLVED WITH DEAR IMGUI.
10639
//   Which is why it is required you put them in your imconfig file (and NOT only before including imgui.h).
10640
//   Otherwise it is possible that different compilation units would see different structure layout.
10641
//   If you don't want to modify imconfig.h you can use the IMGUI_USER_CONFIG define to change filename.
10642
bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)
10643
3
{
10644
3
    bool error = false;
10645
3
    if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && "Mismatched version string!"); }
10646
3
    if (sz_io    != sizeof(ImGuiIO))    { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); }
10647
3
    if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); }
10648
3
    if (sz_vec2  != sizeof(ImVec2))     { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); }
10649
3
    if (sz_vec4  != sizeof(ImVec4))     { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); }
10650
3
    if (sz_vert  != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); }
10651
3
    if (sz_idx   != sizeof(ImDrawIdx))  { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && "Mismatched struct layout!"); }
10652
3
    return !error;
10653
3
}
10654
10655
// Until 1.89 (August 2022, IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos()/SetCursorScreenPos()
10656
// to extend contents size of our parent container (e.g. window contents size, which is used for auto-resizing
10657
// windows, table column contents size used for auto-resizing columns, group size).
10658
// This was causing issues and ambiguities and we needed to retire that.
10659
// From 1.89, extending contents size boundaries REQUIRES AN ITEM TO BE SUBMITTED.
10660
//
10661
//  Previously this would make the window content size ~200x200:
10662
//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();                      // NOT OK ANYMORE
10663
//  Instead, please submit an item:
10664
//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK
10665
//  Alternative:
10666
//    Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK
10667
//
10668
// The assert below detects when the _last_ call in a window was a SetCursorPos() not followed by an Item,
10669
// and with a position that would grow the parent contents size.
10670
//
10671
// Advanced:
10672
// - For reference, old logic was causing issues because it meant that SetCursorScreenPos(GetCursorScreenPos())
10673
//   had a side-effect on layout! In particular this caused problem to compute group boundaries.
10674
//   e.g. BeginGroup() + SomeItem() + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() would cause the
10675
//   group to be taller because auto-sizing generally adds padding on bottom and right side.
10676
// - While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect.
10677
//   Using vertical alignment patterns would frequently trigger this sorts of issue.
10678
// - See https://github.com/ocornut/imgui/issues/5548 for more details.
10679
void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()
10680
0
{
10681
0
    ImGuiContext& g = *GImGui;
10682
0
    ImGuiWindow* window = g.CurrentWindow;
10683
0
    IM_ASSERT(window->DC.IsSetPos);
10684
0
    window->DC.IsSetPos = false;
10685
0
    if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)
10686
0
        return;
10687
0
    if (window->SkipItems)
10688
0
        return;
10689
0
    IM_ASSERT_USER_ERROR(0, "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries.\nPlease submit an item e.g. Dummy() afterwards in order to grow window/parent boundaries.");
10690
10691
    // For reference, the old behavior was essentially:
10692
    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
10693
0
}
10694
10695
static void ImGui::ErrorCheckNewFrameSanityChecks()
10696
80.5k
{
10697
80.5k
    ImGuiContext& g = *GImGui;
10698
10699
    // Check user IM_ASSERT macro
10700
    // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!
10701
    //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
10702
    //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
10703
    // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!
10704
    // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!
10705
80.5k
    if (true) IM_ASSERT(1); else IM_ASSERT(0);
10706
10707
    // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)
10708
    // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.
10709
#ifdef __EMSCRIPTEN__
10710
    if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)
10711
        g.IO.DeltaTime = 0.00001f;
10712
#endif
10713
10714
    // Check user data
10715
    // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)
10716
80.5k
    IM_ASSERT(g.Initialized);
10717
80.5k
    IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && "Need a positive DeltaTime!");
10718
80.5k
    IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
10719
80.5k
    IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && "Invalid DisplaySize value!");
10720
80.5k
    IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && "Invalid style setting!");
10721
80.5k
    IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f                 && "Invalid style setting!");
10722
80.5k
    IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
10723
80.5k
    IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting!");
10724
80.5k
    IM_ASSERT(g.Style.WindowBorderHoverPadding > 0.0f                   && "Invalid style setting!"); // Required otherwise cannot resize from borders.
10725
80.5k
    IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
10726
80.5k
    IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);
10727
80.5k
    IM_ASSERT(g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesNone || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesFull || g.Style.TreeLinesFlags == ImGuiTreeNodeFlags_DrawLinesToNodes);
10728
10729
    // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way.
10730
80.5k
    if (g.IO.ConfigErrorRecovery)
10731
80.5k
        IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL);
10732
10733
80.5k
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
10734
80.5k
    if (g.IO.FontGlobalScale > 1.0f)
10735
80.5k
        IM_ASSERT(g.Style.FontScaleMain == 1.0f && "Since 1.92: use style.FontScaleMain instead of g.IO.FontGlobalScale!");
10736
10737
    // Remap legacy names
10738
80.5k
    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)
10739
0
    {
10740
0
        g.IO.ConfigNavMoveSetMousePos = true;
10741
0
        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavEnableSetMousePos;
10742
0
    }
10743
80.5k
    if (g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)
10744
0
    {
10745
0
        g.IO.ConfigNavCaptureKeyboard = false;
10746
0
        g.IO.ConfigFlags &= ~ImGuiConfigFlags_NavNoCaptureKeyboard;
10747
0
    }
10748
10749
    // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024)
10750
80.5k
    if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl))
10751
0
        g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); };
10752
80.5k
    if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl))
10753
0
        g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); };
10754
80.5k
#endif
10755
80.5k
}
10756
10757
static void ImGui::ErrorCheckEndFrameSanityChecks()
10758
80.5k
{
10759
    // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()
10760
    // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().
10761
    // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will
10762
    // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.
10763
    // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),
10764
    // while still correctly asserting on mid-frame key press events.
10765
80.5k
    ImGuiContext& g = *GImGui;
10766
80.5k
    const ImGuiKeyChord key_mods = GetMergedModsFromKeys();
10767
80.5k
    IM_UNUSED(g);
10768
80.5k
    IM_UNUSED(key_mods);
10769
80.5k
    IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
10770
80.5k
    IM_UNUSED(key_mods);
10771
10772
80.5k
    IM_ASSERT(g.CurrentWindowStack.Size == 1);
10773
80.5k
    IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow);
10774
80.5k
}
10775
10776
// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery.
10777
void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out)
10778
242k
{
10779
242k
    ImGuiContext& g = *GImGui;
10780
242k
    state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size;
10781
242k
    state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size;
10782
242k
    state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack!
10783
242k
    state_out->SizeOfColorStack = (short)g.ColorStack.Size;
10784
242k
    state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size;
10785
242k
    state_out->SizeOfFontStack = (short)g.FontStack.Size;
10786
242k
    state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;
10787
242k
    state_out->SizeOfGroupStack = (short)g.GroupStack.Size;
10788
242k
    state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;
10789
242k
    state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;
10790
242k
    state_out->SizeOfDisabledStack = (short)g.DisabledStackSize;
10791
242k
}
10792
10793
// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery.
10794
// Called by e.g. EndFrame() but may be called for manual recovery.
10795
// Attempt to recover full window stack.
10796
void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in)
10797
80.5k
{
10798
    // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
10799
80.5k
    ImGuiContext& g = *GImGui;
10800
80.5k
    while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044
10801
0
    {
10802
        // Recap:
10803
        // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped.
10804
        // - Always call a matching End() for each Begin() call, regardless of its return value!
10805
        // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS.
10806
        // - We will fix that in a future major update.
10807
0
        ImGuiWindow* window = g.CurrentWindow;
10808
0
        if (window->Flags & ImGuiWindowFlags_ChildWindow)
10809
0
        {
10810
0
            if (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow)
10811
0
            {
10812
0
                IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
10813
0
                EndTable();
10814
0
            }
10815
0
            else
10816
0
            {
10817
0
                IM_ASSERT_USER_ERROR(0, "Missing EndChild()");
10818
0
                EndChild();
10819
0
            }
10820
0
        }
10821
0
        else
10822
0
        {
10823
0
            IM_ASSERT_USER_ERROR(0, "Missing End()");
10824
0
            End();
10825
0
        }
10826
0
    }
10827
80.5k
    if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack)
10828
80.5k
        ErrorRecoveryTryToRecoverWindowState(state_in);
10829
80.5k
}
10830
10831
// Called by e.g. End() but may be called for manual recovery.
10832
// Read '// Error Handling [BETA]' block in imgui_internal.h for details.
10833
// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
10834
void    ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in)
10835
242k
{
10836
242k
    ImGuiContext& g = *GImGui;
10837
10838
242k
    while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) //-V1044
10839
0
    {
10840
0
        IM_ASSERT_USER_ERROR(0, "Missing EndTable()");
10841
0
        EndTable();
10842
0
    }
10843
10844
242k
    ImGuiWindow* window = g.CurrentWindow;
10845
10846
    // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
10847
242k
    while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044
10848
0
    {
10849
0
        IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()");
10850
0
        EndTabBar();
10851
0
    }
10852
242k
    while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) //-V1044
10853
0
    {
10854
0
        IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()");
10855
0
        EndMultiSelect();
10856
0
    }
10857
242k
    if (window->DC.MenuBarAppending) //-V1044
10858
0
    {
10859
0
        IM_ASSERT_USER_ERROR(0, "Missing EndMenuBar()");
10860
0
        EndMenuBar();
10861
0
    }
10862
242k
    while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044
10863
0
    {
10864
0
        IM_ASSERT_USER_ERROR(0, "Missing TreePop()");
10865
0
        TreePop();
10866
0
    }
10867
242k
    while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044
10868
0
    {
10869
0
        IM_ASSERT_USER_ERROR(0, "Missing EndGroup()");
10870
0
        EndGroup();
10871
0
    }
10872
242k
    IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack);
10873
242k
    while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044
10874
0
    {
10875
0
        IM_ASSERT_USER_ERROR(0, "Missing PopID()");
10876
0
        PopID();
10877
0
    }
10878
242k
    while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044
10879
0
    {
10880
0
        IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()");
10881
0
        if (g.CurrentItemFlags & ImGuiItemFlags_Disabled)
10882
0
            EndDisabled();
10883
0
        else
10884
0
        {
10885
0
            EndDisabledOverrideReenable();
10886
0
            g.CurrentWindowStack.back().DisabledOverrideReenable = false;
10887
0
        }
10888
0
    }
10889
242k
    IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack);
10890
242k
    while (g.ColorStack.Size > state_in->SizeOfColorStack) //-V1044
10891
0
    {
10892
0
        IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()");
10893
0
        PopStyleColor();
10894
0
    }
10895
242k
    while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044
10896
0
    {
10897
0
        IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()");
10898
0
        PopItemFlag();
10899
0
    }
10900
242k
    while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044
10901
0
    {
10902
0
        IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()");
10903
0
        PopStyleVar();
10904
0
    }
10905
242k
    while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044
10906
0
    {
10907
0
        IM_ASSERT_USER_ERROR(0, "Missing PopFont()");
10908
0
        PopFont();
10909
0
    }
10910
242k
    while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044
10911
0
    {
10912
0
        IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()");
10913
0
        PopFocusScope();
10914
0
    }
10915
    //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack);
10916
242k
}
10917
10918
bool    ImGui::ErrorLog(const char* msg)
10919
0
{
10920
0
    ImGuiContext& g = *GImGui;
10921
10922
    // Output to debug log
10923
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10924
0
    ImGuiWindow* window = g.CurrentWindow;
10925
10926
0
    if (g.IO.ConfigErrorRecoveryEnableDebugLog)
10927
0
    {
10928
0
        if (g.ErrorFirst)
10929
0
            IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n",
10930
0
                g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip);
10931
0
        IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg);
10932
0
    }
10933
0
    g.ErrorFirst = false;
10934
10935
    // Output to tooltip
10936
0
    if (g.IO.ConfigErrorRecoveryEnableTooltip)
10937
0
    {
10938
0
        if (g.WithinFrameScope && BeginErrorTooltip())
10939
0
        {
10940
0
            if (g.ErrorCountCurrentFrame < 20)
10941
0
            {
10942
0
                Text("In window '%s': %s", window ? window->Name : "NULL", msg);
10943
0
                if (window && (!window->IsFallbackWindow || window->WasActive))
10944
0
                    GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255));
10945
0
            }
10946
0
            if (g.ErrorCountCurrentFrame == 20)
10947
0
                Text("(and more errors)");
10948
            // EndFrame() will amend debug buttons to this window, after all errors have been submitted.
10949
0
            EndErrorTooltip();
10950
0
        }
10951
0
        g.ErrorCountCurrentFrame++;
10952
0
    }
10953
0
#endif
10954
10955
    // Output to callback
10956
0
    if (g.ErrorCallback != NULL)
10957
0
        g.ErrorCallback(&g, g.ErrorCallbackUserData, msg);
10958
10959
    // Return whether we should assert
10960
0
    return g.IO.ConfigErrorRecoveryEnableAssert;
10961
0
}
10962
10963
void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip()
10964
80.5k
{
10965
80.5k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
10966
80.5k
    ImGuiContext& g = *GImGui;
10967
80.5k
    if (g.DebugDrawIdConflictsId != 0 && g.IO.KeyCtrl == false)
10968
0
        g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount;
10969
80.5k
    if (g.DebugDrawIdConflictsId != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip())
10970
0
    {
10971
0
        Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount);
10972
0
        BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!");
10973
0
        BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!");
10974
        //BulletText("Code intending to use duplicate ID may use e.g. PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()"); // Not making this too visible for fear of it being abused.
10975
0
        BulletText("Set io.ConfigDebugHighlightIdConflicts=false to disable this warning in non-programmers builds.");
10976
0
        Separator();
10977
0
        if (g.IO.ConfigDebugHighlightIdConflictsShowItemPicker)
10978
0
        {
10979
0
            Text("(Hold CTRL to: use ");
10980
0
            SameLine(0.0f, 0.0f);
10981
0
            if (SmallButton("Item Picker"))
10982
0
                DebugStartItemPicker();
10983
0
            SameLine(0.0f, 0.0f);
10984
0
            Text(" to break in item call-stack, or ");
10985
0
        }
10986
0
        else
10987
0
        {
10988
0
            Text("(Hold CTRL to ");
10989
0
        }
10990
0
        SameLine(0.0f, 0.0f);
10991
0
        if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL)
10992
0
            g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage");
10993
0
        SameLine(0.0f, 0.0f);
10994
0
        Text(")");
10995
0
        EndErrorTooltip();
10996
0
    }
10997
10998
80.5k
    if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame
10999
0
    {
11000
0
        Separator();
11001
0
        Text("(Hold CTRL to:");
11002
0
        SameLine();
11003
0
        if (SmallButton("Enable Asserts"))
11004
0
            g.IO.ConfigErrorRecoveryEnableAssert = true;
11005
        //SameLine();
11006
        //if (SmallButton("Hide Error Tooltips"))
11007
        //    g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous
11008
0
        SameLine(0, 0);
11009
0
        Text(")");
11010
0
        EndErrorTooltip();
11011
0
    }
11012
80.5k
#endif
11013
80.5k
}
11014
11015
// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it.
11016
bool ImGui::BeginErrorTooltip()
11017
0
{
11018
0
    ImGuiContext& g = *GImGui;
11019
0
    ImGuiWindow* window = FindWindowByName("##Tooltip_Error");
11020
0
    const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive);
11021
0
    PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f));
11022
0
    if (use_locked_pos)
11023
0
        SetNextWindowPos(g.ErrorTooltipLockedPos);
11024
0
    bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
11025
0
    PopStyleColor();
11026
0
    if (is_visible && g.CurrentWindow->BeginCount == 1)
11027
0
    {
11028
0
        SeparatorText("MESSAGE FROM DEAR IMGUI");
11029
0
        BringWindowToDisplayFront(g.CurrentWindow);
11030
0
        BringWindowToFocusFront(g.CurrentWindow);
11031
0
        g.ErrorTooltipLockedPos = GetWindowPos();
11032
0
    }
11033
0
    else if (!is_visible)
11034
0
    {
11035
0
        End();
11036
0
    }
11037
0
    return is_visible;
11038
0
}
11039
11040
void ImGui::EndErrorTooltip()
11041
0
{
11042
0
    End();
11043
0
}
11044
11045
//-----------------------------------------------------------------------------
11046
// [SECTION] ITEM SUBMISSION
11047
//-----------------------------------------------------------------------------
11048
// - KeepAliveID()
11049
// - ItemAdd()
11050
//-----------------------------------------------------------------------------
11051
11052
// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().
11053
void ImGui::KeepAliveID(ImGuiID id)
11054
324k
{
11055
324k
    ImGuiContext& g = *GImGui;
11056
324k
    if (g.ActiveId == id)
11057
0
        g.ActiveIdIsAlive = id;
11058
324k
    if (g.DeactivatedItemData.ID == id)
11059
0
        g.DeactivatedItemData.IsAlive = true;
11060
324k
}
11061
11062
// Declare item bounding box for clipping and interaction.
11063
// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface
11064
// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.
11065
// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN)
11066
IM_MSVC_RUNTIME_CHECKS_OFF
11067
bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)
11068
324k
{
11069
324k
    ImGuiContext& g = *GImGui;
11070
324k
    ImGuiWindow* window = g.CurrentWindow;
11071
11072
    // Set item data
11073
    // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)
11074
324k
    g.LastItemData.ID = id;
11075
324k
    g.LastItemData.Rect = bb;
11076
324k
    g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;
11077
324k
    g.LastItemData.ItemFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;
11078
324k
    g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;
11079
    // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.
11080
11081
324k
    if (id != 0)
11082
324k
    {
11083
324k
        KeepAliveID(id);
11084
11085
        // Directional navigation processing
11086
        // Runs prior to clipping early-out
11087
        //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget
11088
        //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests
11089
        //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of
11090
        //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.
11091
        //      We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able
11092
        //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).
11093
        // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.
11094
        // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.
11095
324k
        if (!(g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav))
11096
324k
        {
11097
            // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test.
11098
324k
            window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);
11099
324k
            if (g.NavId == id || g.NavAnyRequest)
11100
664
                if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)
11101
664
                    if (window == g.NavWindow || ((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened))
11102
664
                        NavProcessItem();
11103
324k
        }
11104
11105
324k
        if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasShortcut)
11106
0
            ItemHandleShortcut(id);
11107
324k
    }
11108
11109
    // Lightweight clear of SetNextItemXXX data.
11110
324k
    g.NextItemData.HasFlags = ImGuiNextItemDataFlags_None;
11111
324k
    g.NextItemData.ItemFlags = ImGuiItemFlags_None;
11112
11113
#ifdef IMGUI_ENABLE_TEST_ENGINE
11114
    if (id != 0)
11115
        IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);
11116
#endif
11117
11118
    // Clipping test
11119
    // (this is an inline copy of IsClippedEx() so we can reuse the is_rect_visible value, otherwise we'd do 'if (IsClippedEx(bb, id)) return false')
11120
    // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts)
11121
324k
    const bool is_rect_visible = bb.Overlaps(window->ClipRect);
11122
324k
    if (!is_rect_visible)
11123
2
        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId))
11124
2
            if (!g.ItemUnclipByLog)
11125
2
                return false;
11126
11127
    // [DEBUG]
11128
324k
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
11129
324k
    if (id != 0)
11130
324k
    {
11131
324k
        if (id == g.DebugLocateId)
11132
0
            DebugLocateItemResolveWithLastItem();
11133
11134
        // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something".
11135
        // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something".
11136
        // READ THE FAQ: https://dearimgui.com/faq
11137
324k
        IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!");
11138
11139
        // [DEBUG] Highlight all conflicts WITHOUT needing to hover. THIS WILL SLOW DOWN DEAR IMGUI. DON'T KEEP ACTIVATED.
11140
        // This will only work for items submitted with ItemAdd(). Some very rare/odd/unrecommended code patterns are calling ButtonBehavior() without ItemAdd().
11141
#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
11142
        if ((g.LastItemData.ItemFlags & ImGuiItemFlags_AllowDuplicateId) == 0)
11143
        {
11144
            int* p_alive = g.DebugDrawIdConflictsAliveCount.GetIntRef(id, -1); // Could halve lookups if we knew ImGuiStorage can store 64-bit, or by storing FrameCount as 30-bits + highlight as 2-bits. But the point is that we should not pretend that this is fast.
11145
            int* p_highlight = g.DebugDrawIdConflictsHighlightSet.GetIntRef(id, -1);
11146
            if (*p_alive == g.FrameCount)
11147
                *p_highlight = g.FrameCount;
11148
            *p_alive = g.FrameCount;
11149
            if (*p_highlight >= g.FrameCount - 1)
11150
                window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f);
11151
        }
11152
#endif
11153
324k
    }
11154
    //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]
11155
    //if ((g.LastItemData.ItemFlags & ImGuiItemFlags_NoNav) == 0)
11156
    //    window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]
11157
324k
#endif
11158
11159
324k
    if (id != 0 && g.DeactivatedItemData.ID == id)
11160
0
        g.DeactivatedItemData.ElapseFrame = g.FrameCount;
11161
11162
    // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)
11163
324k
    if (is_rect_visible)
11164
324k
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;
11165
324k
    if (IsMouseHoveringRect(bb.Min, bb.Max))
11166
1.15k
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;
11167
324k
    return true;
11168
324k
}
11169
IM_MSVC_RUNTIME_CHECKS_RESTORE
11170
11171
//-----------------------------------------------------------------------------
11172
// [SECTION] LAYOUT
11173
//-----------------------------------------------------------------------------
11174
// - ItemSize()
11175
// - SameLine()
11176
// - GetCursorScreenPos()
11177
// - SetCursorScreenPos()
11178
// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()
11179
// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()
11180
// - GetCursorStartPos()
11181
// - Indent()
11182
// - Unindent()
11183
// - SetNextItemWidth()
11184
// - PushItemWidth()
11185
// - PushMultiItemsWidths()
11186
// - PopItemWidth()
11187
// - CalcItemWidth()
11188
// - CalcItemSize()
11189
// - GetTextLineHeight()
11190
// - GetTextLineHeightWithSpacing()
11191
// - GetFrameHeight()
11192
// - GetFrameHeightWithSpacing()
11193
// - GetContentRegionMax()
11194
// - GetContentRegionAvail(),
11195
// - BeginGroup()
11196
// - EndGroup()
11197
// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.
11198
//-----------------------------------------------------------------------------
11199
11200
// Advance cursor given item size for layout.
11201
// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.
11202
// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.
11203
// THIS IS IN THE PERFORMANCE CRITICAL PATH.
11204
IM_MSVC_RUNTIME_CHECKS_OFF
11205
void ImGui::ItemSize(const ImVec2& size, float text_baseline_y)
11206
243k
{
11207
243k
    ImGuiContext& g = *GImGui;
11208
243k
    ImGuiWindow* window = g.CurrentWindow;
11209
243k
    if (window->SkipItems)
11210
0
        return;
11211
11212
    // We increase the height in this function to accommodate for baseline offset.
11213
    // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,
11214
    // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.
11215
243k
    const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;
11216
11217
243k
    const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;
11218
243k
    const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);
11219
11220
    // Always align ourselves on pixel boundaries
11221
    //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]
11222
243k
    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;
11223
243k
    window->DC.CursorPosPrevLine.y = line_y1;
11224
243k
    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line
11225
243k
    window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y);                       // Next line
11226
243k
    window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);
11227
243k
    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);
11228
    //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]
11229
11230
243k
    window->DC.PrevLineSize.y = line_height;
11231
243k
    window->DC.CurrLineSize.y = 0.0f;
11232
243k
    window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);
11233
243k
    window->DC.CurrLineTextBaseOffset = 0.0f;
11234
243k
    window->DC.IsSameLine = window->DC.IsSetPos = false;
11235
11236
    // Horizontal layout mode
11237
243k
    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
11238
241k
        SameLine();
11239
243k
}
11240
IM_MSVC_RUNTIME_CHECKS_RESTORE
11241
11242
// Gets back to previous line and continue with horizontal layout
11243
//      offset_from_start_x == 0 : follow right after previous item
11244
//      offset_from_start_x != 0 : align to specified x position (relative to window/group left)
11245
//      spacing_w < 0            : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0
11246
//      spacing_w >= 0           : enforce spacing amount
11247
void ImGui::SameLine(float offset_from_start_x, float spacing_w)
11248
241k
{
11249
241k
    ImGuiContext& g = *GImGui;
11250
241k
    ImGuiWindow* window = g.CurrentWindow;
11251
241k
    if (window->SkipItems)
11252
0
        return;
11253
11254
241k
    if (offset_from_start_x != 0.0f)
11255
0
    {
11256
0
        if (spacing_w < 0.0f)
11257
0
            spacing_w = 0.0f;
11258
0
        window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;
11259
0
        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11260
0
    }
11261
241k
    else
11262
241k
    {
11263
241k
        if (spacing_w < 0.0f)
11264
241k
            spacing_w = g.Style.ItemSpacing.x;
11265
241k
        window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;
11266
241k
        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;
11267
241k
    }
11268
241k
    window->DC.CurrLineSize = window->DC.PrevLineSize;
11269
241k
    window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
11270
241k
    window->DC.IsSameLine = true;
11271
241k
}
11272
11273
ImVec2 ImGui::GetCursorScreenPos()
11274
0
{
11275
0
    ImGuiWindow* window = GetCurrentWindowRead();
11276
0
    return window->DC.CursorPos;
11277
0
}
11278
11279
void ImGui::SetCursorScreenPos(const ImVec2& pos)
11280
0
{
11281
0
    ImGuiWindow* window = GetCurrentWindow();
11282
0
    window->DC.CursorPos = pos;
11283
    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11284
0
    window->DC.IsSetPos = true;
11285
0
}
11286
11287
// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.
11288
// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.
11289
ImVec2 ImGui::GetCursorPos()
11290
0
{
11291
0
    ImGuiWindow* window = GetCurrentWindowRead();
11292
0
    return window->DC.CursorPos - window->Pos + window->Scroll;
11293
0
}
11294
11295
float ImGui::GetCursorPosX()
11296
0
{
11297
0
    ImGuiWindow* window = GetCurrentWindowRead();
11298
0
    return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;
11299
0
}
11300
11301
float ImGui::GetCursorPosY()
11302
0
{
11303
0
    ImGuiWindow* window = GetCurrentWindowRead();
11304
0
    return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;
11305
0
}
11306
11307
void ImGui::SetCursorPos(const ImVec2& local_pos)
11308
0
{
11309
0
    ImGuiWindow* window = GetCurrentWindow();
11310
0
    window->DC.CursorPos = window->Pos - window->Scroll + local_pos;
11311
    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);
11312
0
    window->DC.IsSetPos = true;
11313
0
}
11314
11315
void ImGui::SetCursorPosX(float x)
11316
0
{
11317
0
    ImGuiWindow* window = GetCurrentWindow();
11318
0
    window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;
11319
    //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);
11320
0
    window->DC.IsSetPos = true;
11321
0
}
11322
11323
void ImGui::SetCursorPosY(float y)
11324
0
{
11325
0
    ImGuiWindow* window = GetCurrentWindow();
11326
0
    window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;
11327
    //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);
11328
0
    window->DC.IsSetPos = true;
11329
0
}
11330
11331
ImVec2 ImGui::GetCursorStartPos()
11332
0
{
11333
0
    ImGuiWindow* window = GetCurrentWindowRead();
11334
0
    return window->DC.CursorStartPos - window->Pos;
11335
0
}
11336
11337
void ImGui::Indent(float indent_w)
11338
0
{
11339
0
    ImGuiContext& g = *GImGui;
11340
0
    ImGuiWindow* window = GetCurrentWindow();
11341
0
    window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11342
0
    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11343
0
}
11344
11345
void ImGui::Unindent(float indent_w)
11346
0
{
11347
0
    ImGuiContext& g = *GImGui;
11348
0
    ImGuiWindow* window = GetCurrentWindow();
11349
0
    window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;
11350
0
    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
11351
0
}
11352
11353
// Affect large frame+labels widgets only.
11354
void ImGui::SetNextItemWidth(float item_width)
11355
0
{
11356
0
    ImGuiContext& g = *GImGui;
11357
0
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasWidth;
11358
0
    g.NextItemData.Width = item_width;
11359
0
}
11360
11361
// FIXME: Remove the == 0.0f behavior?
11362
void ImGui::PushItemWidth(float item_width)
11363
0
{
11364
0
    ImGuiContext& g = *GImGui;
11365
0
    ImGuiWindow* window = g.CurrentWindow;
11366
0
    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11367
0
    window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);
11368
0
    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11369
0
}
11370
11371
void ImGui::PushMultiItemsWidths(int components, float w_full)
11372
0
{
11373
0
    ImGuiContext& g = *GImGui;
11374
0
    ImGuiWindow* window = g.CurrentWindow;
11375
0
    IM_ASSERT(components > 0);
11376
0
    const ImGuiStyle& style = g.Style;
11377
0
    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width
11378
0
    float w_items = w_full - style.ItemInnerSpacing.x * (components - 1);
11379
0
    float prev_split = w_items;
11380
0
    for (int i = components - 1; i > 0; i--)
11381
0
    {
11382
0
        float next_split = IM_TRUNC(w_items * i / components);
11383
0
        window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f));
11384
0
        prev_split = next_split;
11385
0
    }
11386
0
    window->DC.ItemWidth = ImMax(prev_split, 1.0f);
11387
0
    g.NextItemData.HasFlags &= ~ImGuiNextItemDataFlags_HasWidth;
11388
0
}
11389
11390
void ImGui::PopItemWidth()
11391
0
{
11392
0
    ImGuiContext& g = *GImGui;
11393
0
    ImGuiWindow* window = g.CurrentWindow;
11394
0
    if (window->DC.ItemWidthStack.Size <= 0)
11395
0
    {
11396
0
        IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!");
11397
0
        return;
11398
0
    }
11399
0
    window->DC.ItemWidth = window->DC.ItemWidthStack.back();
11400
0
    window->DC.ItemWidthStack.pop_back();
11401
0
}
11402
11403
// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().
11404
// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()
11405
float ImGui::CalcItemWidth()
11406
0
{
11407
0
    ImGuiContext& g = *GImGui;
11408
0
    ImGuiWindow* window = g.CurrentWindow;
11409
0
    float w;
11410
0
    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
11411
0
        w = g.NextItemData.Width;
11412
0
    else
11413
0
        w = window->DC.ItemWidth;
11414
0
    if (w < 0.0f)
11415
0
    {
11416
0
        float region_avail_x = GetContentRegionAvail().x;
11417
0
        w = ImMax(1.0f, region_avail_x + w);
11418
0
    }
11419
0
    w = IM_TRUNC(w);
11420
0
    return w;
11421
0
}
11422
11423
// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().
11424
// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.
11425
// Note that only CalcItemWidth() is publicly exposed.
11426
// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)
11427
ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)
11428
0
{
11429
0
    ImVec2 avail;
11430
0
    if (size.x < 0.0f || size.y < 0.0f)
11431
0
        avail = GetContentRegionAvail();
11432
11433
0
    if (size.x == 0.0f)
11434
0
        size.x = default_w;
11435
0
    else if (size.x < 0.0f)
11436
0
        size.x = ImMax(4.0f, avail.x + size.x); // <-- size.x is negative here so we are subtracting
11437
11438
0
    if (size.y == 0.0f)
11439
0
        size.y = default_h;
11440
0
    else if (size.y < 0.0f)
11441
0
        size.y = ImMax(4.0f, avail.y + size.y); // <-- size.y is negative here so we are subtracting
11442
11443
0
    return size;
11444
0
}
11445
11446
float ImGui::GetTextLineHeight()
11447
0
{
11448
0
    ImGuiContext& g = *GImGui;
11449
0
    return g.FontSize;
11450
0
}
11451
11452
float ImGui::GetTextLineHeightWithSpacing()
11453
0
{
11454
0
    ImGuiContext& g = *GImGui;
11455
0
    return g.FontSize + g.Style.ItemSpacing.y;
11456
0
}
11457
11458
float ImGui::GetFrameHeight()
11459
80.5k
{
11460
80.5k
    ImGuiContext& g = *GImGui;
11461
80.5k
    return g.FontSize + g.Style.FramePadding.y * 2.0f;
11462
80.5k
}
11463
11464
float ImGui::GetFrameHeightWithSpacing()
11465
0
{
11466
0
    ImGuiContext& g = *GImGui;
11467
0
    return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;
11468
0
}
11469
11470
ImVec2 ImGui::GetContentRegionAvail()
11471
1.98k
{
11472
1.98k
    ImGuiContext& g = *GImGui;
11473
1.98k
    ImGuiWindow* window = g.CurrentWindow;
11474
1.98k
    ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;
11475
1.98k
    return mx - window->DC.CursorPos;
11476
1.98k
}
11477
11478
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
11479
11480
// You should never need those functions. Always use GetCursorScreenPos() and GetContentRegionAvail()!
11481
// They are bizarre local-coordinates which don't play well with scrolling.
11482
ImVec2 ImGui::GetContentRegionMax()
11483
0
{
11484
0
    return GetContentRegionAvail() + GetCursorScreenPos() - GetWindowPos();
11485
0
}
11486
11487
ImVec2 ImGui::GetWindowContentRegionMin()
11488
0
{
11489
0
    ImGuiWindow* window = GImGui->CurrentWindow;
11490
0
    return window->ContentRegionRect.Min - window->Pos;
11491
0
}
11492
11493
ImVec2 ImGui::GetWindowContentRegionMax()
11494
0
{
11495
0
    ImGuiWindow* window = GImGui->CurrentWindow;
11496
0
    return window->ContentRegionRect.Max - window->Pos;
11497
0
}
11498
#endif
11499
11500
// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
11501
// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.
11502
// FIXME-OPT: Could we safely early out on ->SkipItems?
11503
void ImGui::BeginGroup()
11504
80.5k
{
11505
80.5k
    ImGuiContext& g = *GImGui;
11506
80.5k
    ImGuiWindow* window = g.CurrentWindow;
11507
11508
80.5k
    g.GroupStack.resize(g.GroupStack.Size + 1);
11509
80.5k
    ImGuiGroupData& group_data = g.GroupStack.back();
11510
80.5k
    group_data.WindowID = window->ID;
11511
80.5k
    group_data.BackupCursorPos = window->DC.CursorPos;
11512
80.5k
    group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
11513
80.5k
    group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;
11514
80.5k
    group_data.BackupIndent = window->DC.Indent;
11515
80.5k
    group_data.BackupGroupOffset = window->DC.GroupOffset;
11516
80.5k
    group_data.BackupCurrLineSize = window->DC.CurrLineSize;
11517
80.5k
    group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;
11518
80.5k
    group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;
11519
80.5k
    group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;
11520
80.5k
    group_data.BackupIsSameLine = window->DC.IsSameLine;
11521
80.5k
    group_data.BackupDeactivatedIdIsAlive = g.DeactivatedItemData.IsAlive;
11522
80.5k
    group_data.EmitItem = true;
11523
11524
80.5k
    window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;
11525
80.5k
    window->DC.Indent = window->DC.GroupOffset;
11526
80.5k
    window->DC.CursorMaxPos = window->DC.CursorPos;
11527
80.5k
    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
11528
80.5k
    if (g.LogEnabled)
11529
0
        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11530
80.5k
}
11531
11532
void ImGui::EndGroup()
11533
80.5k
{
11534
80.5k
    ImGuiContext& g = *GImGui;
11535
80.5k
    ImGuiWindow* window = g.CurrentWindow;
11536
80.5k
    IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls
11537
11538
80.5k
    ImGuiGroupData& group_data = g.GroupStack.back();
11539
80.5k
    IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?
11540
11541
80.5k
    if (window->DC.IsSetPos)
11542
0
        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
11543
11544
    // Include LastItemData.Rect.Max as a workaround for e.g. EndTable() undershooting with CursorMaxPos report. (#7543)
11545
80.5k
    ImRect group_bb(group_data.BackupCursorPos, ImMax(ImMax(window->DC.CursorMaxPos, g.LastItemData.Rect.Max), group_data.BackupCursorPos));
11546
80.5k
    window->DC.CursorPos = group_data.BackupCursorPos;
11547
80.5k
    window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;
11548
80.5k
    window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, group_bb.Max);
11549
80.5k
    window->DC.Indent = group_data.BackupIndent;
11550
80.5k
    window->DC.GroupOffset = group_data.BackupGroupOffset;
11551
80.5k
    window->DC.CurrLineSize = group_data.BackupCurrLineSize;
11552
80.5k
    window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;
11553
80.5k
    window->DC.IsSameLine = group_data.BackupIsSameLine;
11554
80.5k
    if (g.LogEnabled)
11555
0
        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return
11556
11557
80.5k
    if (!group_data.EmitItem)
11558
80.5k
    {
11559
80.5k
        g.GroupStack.pop_back();
11560
80.5k
        return;
11561
80.5k
    }
11562
11563
0
    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.
11564
0
    ItemSize(group_bb.GetSize());
11565
0
    ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);
11566
11567
    // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.
11568
    // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.
11569
    // Also if you grep for LastItemId you'll notice it is only used in that context.
11570
    // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)
11571
0
    const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;
11572
0
    const bool group_contains_deactivated_id = (group_data.BackupDeactivatedIdIsAlive == false) && (g.DeactivatedItemData.IsAlive == true);
11573
0
    if (group_contains_curr_active_id)
11574
0
        g.LastItemData.ID = g.ActiveId;
11575
0
    else if (group_contains_deactivated_id)
11576
0
        g.LastItemData.ID = g.DeactivatedItemData.ID;
11577
0
    g.LastItemData.Rect = group_bb;
11578
11579
    // Forward Hovered flag
11580
0
    const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;
11581
0
    if (group_contains_curr_hovered_id)
11582
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
11583
11584
    // Forward Edited flag
11585
0
    if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)
11586
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;
11587
11588
    // Forward Deactivated flag
11589
0
    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;
11590
0
    if (group_contains_deactivated_id)
11591
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;
11592
11593
0
    g.GroupStack.pop_back();
11594
0
    if (g.DebugShowGroupRects)
11595
0
        window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]
11596
0
}
11597
11598
11599
//-----------------------------------------------------------------------------
11600
// [SECTION] SCROLLING
11601
//-----------------------------------------------------------------------------
11602
11603
// Helper to snap on edges when aiming at an item very close to the edge,
11604
// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.
11605
// When we refactor the scrolling API this may be configurable with a flag?
11606
// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.
11607
static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)
11608
0
{
11609
0
    if (target <= snap_min + snap_threshold)
11610
0
        return ImLerp(snap_min, target, center_ratio);
11611
0
    if (target >= snap_max - snap_threshold)
11612
0
        return ImLerp(target, snap_max, center_ratio);
11613
0
    return target;
11614
0
}
11615
11616
static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
11617
161k
{
11618
161k
    ImVec2 scroll = window->Scroll;
11619
161k
    ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
11620
485k
    for (int axis = 0; axis < 2; axis++)
11621
323k
    {
11622
323k
        if (window->ScrollTarget[axis] < FLT_MAX)
11623
0
        {
11624
0
            float center_ratio = window->ScrollTargetCenterRatio[axis];
11625
0
            float scroll_target = window->ScrollTarget[axis];
11626
0
            if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
11627
0
            {
11628
0
                float snap_min = 0.0f;
11629
0
                float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
11630
0
                scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
11631
0
            }
11632
0
            scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
11633
0
        }
11634
323k
        scroll[axis] = ImRound64(ImMax(scroll[axis], 0.0f));
11635
323k
        if (!window->Collapsed && !window->SkipItems)
11636
323k
            scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
11637
323k
    }
11638
161k
    return scroll;
11639
161k
}
11640
11641
void ImGui::ScrollToItem(ImGuiScrollFlags flags)
11642
0
{
11643
0
    ImGuiContext& g = *GImGui;
11644
0
    ImGuiWindow* window = g.CurrentWindow;
11645
0
    ScrollToRectEx(window, g.LastItemData.NavRect, flags);
11646
0
}
11647
11648
void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11649
0
{
11650
0
    ScrollToRectEx(window, item_rect, flags);
11651
0
}
11652
11653
// Scroll to keep newly navigated item fully into view
11654
ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)
11655
0
{
11656
0
    ImGuiContext& g = *GImGui;
11657
0
    ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));
11658
0
    scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);
11659
0
    scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);
11660
    //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]
11661
    //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]
11662
11663
    // Check that only one behavior is selected per axis
11664
0
    IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));
11665
0
    IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));
11666
11667
    // Defaults
11668
0
    ImGuiScrollFlags in_flags = flags;
11669
0
    if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)
11670
0
        flags |= ImGuiScrollFlags_KeepVisibleEdgeX;
11671
0
    if ((flags & ImGuiScrollFlags_MaskY_) == 0)
11672
0
        flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;
11673
11674
0
    const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;
11675
0
    const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;
11676
0
    const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
11677
0
    const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;
11678
11679
0
    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)
11680
0
    {
11681
0
        if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)
11682
0
            SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);
11683
0
        else if (item_rect.Max.x >= scroll_rect.Max.x)
11684
0
            SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);
11685
0
    }
11686
0
    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))
11687
0
    {
11688
0
        if (can_be_fully_visible_x)
11689
0
            SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);
11690
0
        else
11691
0
            SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);
11692
0
    }
11693
11694
0
    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)
11695
0
    {
11696
0
        if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)
11697
0
            SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);
11698
0
        else if (item_rect.Max.y >= scroll_rect.Max.y)
11699
0
            SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);
11700
0
    }
11701
0
    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))
11702
0
    {
11703
0
        if (can_be_fully_visible_y)
11704
0
            SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);
11705
0
        else
11706
0
            SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);
11707
0
    }
11708
11709
0
    ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
11710
0
    ImVec2 delta_scroll = next_scroll - window->Scroll;
11711
11712
    // Also scroll parent window to keep us into view if necessary
11713
0
    if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))
11714
0
    {
11715
        // FIXME-SCROLL: May be an option?
11716
0
        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)
11717
0
            in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;
11718
0
        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)
11719
0
            in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;
11720
0
        delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);
11721
0
    }
11722
11723
0
    return delta_scroll;
11724
0
}
11725
11726
float ImGui::GetScrollX()
11727
0
{
11728
0
    ImGuiWindow* window = GImGui->CurrentWindow;
11729
0
    return window->Scroll.x;
11730
0
}
11731
11732
float ImGui::GetScrollY()
11733
0
{
11734
0
    ImGuiWindow* window = GImGui->CurrentWindow;
11735
0
    return window->Scroll.y;
11736
0
}
11737
11738
float ImGui::GetScrollMaxX()
11739
0
{
11740
0
    ImGuiWindow* window = GImGui->CurrentWindow;
11741
0
    return window->ScrollMax.x;
11742
0
}
11743
11744
float ImGui::GetScrollMaxY()
11745
0
{
11746
0
    ImGuiWindow* window = GImGui->CurrentWindow;
11747
0
    return window->ScrollMax.y;
11748
0
}
11749
11750
void ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)
11751
0
{
11752
0
    window->ScrollTarget.x = scroll_x;
11753
0
    window->ScrollTargetCenterRatio.x = 0.0f;
11754
0
    window->ScrollTargetEdgeSnapDist.x = 0.0f;
11755
0
}
11756
11757
void ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)
11758
0
{
11759
0
    window->ScrollTarget.y = scroll_y;
11760
0
    window->ScrollTargetCenterRatio.y = 0.0f;
11761
0
    window->ScrollTargetEdgeSnapDist.y = 0.0f;
11762
0
}
11763
11764
void ImGui::SetScrollX(float scroll_x)
11765
0
{
11766
0
    ImGuiContext& g = *GImGui;
11767
0
    SetScrollX(g.CurrentWindow, scroll_x);
11768
0
}
11769
11770
void ImGui::SetScrollY(float scroll_y)
11771
0
{
11772
0
    ImGuiContext& g = *GImGui;
11773
0
    SetScrollY(g.CurrentWindow, scroll_y);
11774
0
}
11775
11776
// Note that a local position will vary depending on initial scroll value,
11777
// This is a little bit confusing so bear with us:
11778
//  - local_pos = (absolution_pos - window->Pos)
11779
//  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,
11780
//    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.
11781
//  - They mostly exist because of legacy API.
11782
// Following the rules above, when trying to work with scrolling code, consider that:
11783
//  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!
11784
//  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense
11785
// We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size
11786
void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)
11787
0
{
11788
0
    IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);
11789
0
    window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset
11790
0
    window->ScrollTargetCenterRatio.x = center_x_ratio;
11791
0
    window->ScrollTargetEdgeSnapDist.x = 0.0f;
11792
0
}
11793
11794
void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)
11795
0
{
11796
0
    IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);
11797
0
    window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset
11798
0
    window->ScrollTargetCenterRatio.y = center_y_ratio;
11799
0
    window->ScrollTargetEdgeSnapDist.y = 0.0f;
11800
0
}
11801
11802
void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
11803
0
{
11804
0
    ImGuiContext& g = *GImGui;
11805
0
    SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);
11806
0
}
11807
11808
void ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)
11809
0
{
11810
0
    ImGuiContext& g = *GImGui;
11811
0
    SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);
11812
0
}
11813
11814
// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.
11815
void ImGui::SetScrollHereX(float center_x_ratio)
11816
0
{
11817
0
    ImGuiContext& g = *GImGui;
11818
0
    ImGuiWindow* window = g.CurrentWindow;
11819
0
    float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);
11820
0
    float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);
11821
0
    SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos
11822
11823
    // Tweak: snap on edges when aiming at an item very close to the edge
11824
0
    window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);
11825
0
}
11826
11827
// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.
11828
void ImGui::SetScrollHereY(float center_y_ratio)
11829
0
{
11830
0
    ImGuiContext& g = *GImGui;
11831
0
    ImGuiWindow* window = g.CurrentWindow;
11832
0
    float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);
11833
0
    float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);
11834
0
    SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos
11835
11836
    // Tweak: snap on edges when aiming at an item very close to the edge
11837
0
    window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);
11838
0
}
11839
11840
//-----------------------------------------------------------------------------
11841
// [SECTION] TOOLTIPS
11842
//-----------------------------------------------------------------------------
11843
11844
bool ImGui::BeginTooltip()
11845
0
{
11846
0
    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
11847
0
}
11848
11849
bool ImGui::BeginItemTooltip()
11850
0
{
11851
0
    if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11852
0
        return false;
11853
0
    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
11854
0
}
11855
11856
bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
11857
0
{
11858
0
    ImGuiContext& g = *GImGui;
11859
11860
0
    const bool is_dragdrop_tooltip = g.DragDropWithinSource || g.DragDropWithinTarget;
11861
0
    if (is_dragdrop_tooltip)
11862
0
    {
11863
        // Drag and Drop tooltips are positioning differently than other tooltips:
11864
        // - offset visibility to increase visibility around mouse.
11865
        // - never clamp within outer viewport boundary.
11866
        // We call SetNextWindowPos() to enforce position and disable clamping.
11867
        // See FindBestWindowPosForPopup() for positioning logic of other tooltips (not drag and drop ones).
11868
        //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
11869
0
        const bool is_touchscreen = (g.IO.MouseSource == ImGuiMouseSource_TouchScreen);
11870
0
        if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)
11871
0
        {
11872
0
            ImVec2 tooltip_pos = is_touchscreen ? (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_TOUCH * g.Style.MouseCursorScale) : (g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET_MOUSE * g.Style.MouseCursorScale);
11873
0
            ImVec2 tooltip_pivot = is_touchscreen ? TOOLTIP_DEFAULT_PIVOT_TOUCH : ImVec2(0.0f, 0.0f);
11874
0
            SetNextWindowPos(tooltip_pos, ImGuiCond_None, tooltip_pivot);
11875
0
        }
11876
11877
0
        SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
11878
        //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
11879
0
        tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
11880
0
    }
11881
11882
0
    const char* window_name_template = is_dragdrop_tooltip ? "##Tooltip_DragDrop_%02d" : "##Tooltip_%02d";
11883
0
    char window_name[32];
11884
0
    ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, g.TooltipOverrideCount);
11885
0
    if ((tooltip_flags & ImGuiTooltipFlags_OverridePrevious) && g.TooltipPreviousWindow != NULL && g.TooltipPreviousWindow->Active)
11886
0
    {
11887
        // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one.
11888
        //IMGUI_DEBUG_LOG("[tooltip] '%s' already active, using +1 for this frame\n", window_name);
11889
0
        SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow);
11890
0
        ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount);
11891
0
    }
11892
0
    ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
11893
0
    Begin(window_name, NULL, flags | extra_window_flags);
11894
    // 2023-03-09: Added bool return value to the API, but currently always returning true.
11895
    // If this ever returns false we need to update BeginDragDropSource() accordingly.
11896
    //if (!ret)
11897
    //    End();
11898
    //return ret;
11899
0
    return true;
11900
0
}
11901
11902
void ImGui::EndTooltip()
11903
0
{
11904
0
    IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls
11905
0
    End();
11906
0
}
11907
11908
void ImGui::SetTooltip(const char* fmt, ...)
11909
0
{
11910
0
    va_list args;
11911
0
    va_start(args, fmt);
11912
0
    SetTooltipV(fmt, args);
11913
0
    va_end(args);
11914
0
}
11915
11916
void ImGui::SetTooltipV(const char* fmt, va_list args)
11917
0
{
11918
0
    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
11919
0
        return;
11920
0
    TextV(fmt, args);
11921
0
    EndTooltip();
11922
0
}
11923
11924
// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
11925
// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
11926
void ImGui::SetItemTooltip(const char* fmt, ...)
11927
0
{
11928
0
    va_list args;
11929
0
    va_start(args, fmt);
11930
0
    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11931
0
        SetTooltipV(fmt, args);
11932
0
    va_end(args);
11933
0
}
11934
11935
void ImGui::SetItemTooltipV(const char* fmt, va_list args)
11936
0
{
11937
0
    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
11938
0
        SetTooltipV(fmt, args);
11939
0
}
11940
11941
11942
//-----------------------------------------------------------------------------
11943
// [SECTION] POPUPS
11944
//-----------------------------------------------------------------------------
11945
11946
// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel
11947
bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)
11948
242k
{
11949
242k
    ImGuiContext& g = *GImGui;
11950
242k
    if (popup_flags & ImGuiPopupFlags_AnyPopupId)
11951
0
    {
11952
        // Return true if any popup is open at the current BeginPopup() level of the popup stack
11953
        // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.
11954
0
        IM_ASSERT(id == 0);
11955
0
        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
11956
0
            return g.OpenPopupStack.Size > 0;
11957
0
        else
11958
0
            return g.OpenPopupStack.Size > g.BeginPopupStack.Size;
11959
0
    }
11960
242k
    else
11961
242k
    {
11962
242k
        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)
11963
0
        {
11964
            // Return true if the popup is open anywhere in the popup stack
11965
0
            for (int n = 0; n < g.OpenPopupStack.Size; n++)
11966
0
                if (g.OpenPopupStack[n].PopupId == id)
11967
0
                    return true;
11968
0
            return false;
11969
0
        }
11970
242k
        else
11971
242k
        {
11972
            // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)
11973
242k
            return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;
11974
242k
        }
11975
242k
    }
11976
242k
}
11977
11978
bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)
11979
0
{
11980
0
    ImGuiContext& g = *GImGui;
11981
0
    ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);
11982
0
    if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)
11983
0
        IM_ASSERT(0 && "Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel."); // But non-string version is legal and used internally
11984
0
    return IsPopupOpen(id, popup_flags);
11985
0
}
11986
11987
// Also see FindBlockingModal(NULL)
11988
ImGuiWindow* ImGui::GetTopMostPopupModal()
11989
241k
{
11990
241k
    ImGuiContext& g = *GImGui;
11991
243k
    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
11992
1.97k
        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
11993
1.97k
            if (popup->Flags & ImGuiWindowFlags_Modal)
11994
0
                return popup;
11995
241k
    return NULL;
11996
241k
}
11997
11998
// See Demo->Stacked Modal to confirm what this is for.
11999
ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()
12000
80.5k
{
12001
80.5k
    ImGuiContext& g = *GImGui;
12002
81.2k
    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)
12003
659
        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)
12004
659
            if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))
12005
0
                return popup;
12006
80.5k
    return NULL;
12007
80.5k
}
12008
12009
12010
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
12011
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
12012
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
12013
// - WindowA            // FindBlockingModal() returns Modal1
12014
//   - WindowB          //                  .. returns Modal1
12015
//   - Modal1           //                  .. returns Modal2
12016
//      - WindowC       //                  .. returns Modal2
12017
//          - WindowD   //                  .. returns Modal2
12018
//          - Modal2    //                  .. returns Modal2
12019
//            - WindowE //                  .. returns NULL
12020
// Notes:
12021
// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
12022
//   Only difference is here we check for ->Active/WasActive but it may be unnecessary.
12023
ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
12024
6
{
12025
6
    ImGuiContext& g = *GImGui;
12026
6
    if (g.OpenPopupStack.Size <= 0)
12027
5
        return NULL;
12028
12029
    // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
12030
1
    for (ImGuiPopupData& popup_data : g.OpenPopupStack)
12031
1
    {
12032
1
        ImGuiWindow* popup_window = popup_data.Window;
12033
1
        if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
12034
1
            continue;
12035
0
        if (!popup_window->Active && !popup_window->WasActive)  // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.
12036
0
            continue;
12037
0
        if (window == NULL)                                     // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
12038
0
            return popup_window;
12039
0
        if (IsWindowWithinBeginStackOf(window, popup_window))   // Window may be over modal
12040
0
            continue;
12041
0
        return popup_window;                                    // Place window right below first block modal
12042
0
    }
12043
1
    return NULL;
12044
1
}
12045
12046
void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)
12047
1
{
12048
1
    ImGuiContext& g = *GImGui;
12049
1
    ImGuiID id = g.CurrentWindow->GetID(str_id);
12050
1
    IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id);
12051
1
    OpenPopupEx(id, popup_flags);
12052
1
}
12053
12054
void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)
12055
0
{
12056
0
    OpenPopupEx(id, popup_flags);
12057
0
}
12058
12059
// Mark popup as open (toggle toward open state).
12060
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
12061
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
12062
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
12063
void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)
12064
1
{
12065
1
    ImGuiContext& g = *GImGui;
12066
1
    ImGuiWindow* parent_window = g.CurrentWindow;
12067
1
    const int current_stack_size = g.BeginPopupStack.Size;
12068
12069
1
    if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)
12070
0
        if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))
12071
0
            return;
12072
12073
1
    ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.
12074
1
    popup_ref.PopupId = id;
12075
1
    popup_ref.Window = NULL;
12076
1
    popup_ref.RestoreNavWindow = g.NavWindow;           // When popup closes focus may be restored to NavWindow (depend on window type).
12077
1
    popup_ref.OpenFrameCount = g.FrameCount;
12078
1
    popup_ref.OpenParentId = parent_window->IDStack.back();
12079
1
    popup_ref.OpenPopupPos = NavCalcPreferredRefPos();
12080
1
    popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;
12081
12082
1
    IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopupEx(0x%08X)\n", id);
12083
1
    if (g.OpenPopupStack.Size < current_stack_size + 1)
12084
1
    {
12085
1
        g.OpenPopupStack.push_back(popup_ref);
12086
1
    }
12087
0
    else
12088
0
    {
12089
        // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake!
12090
        // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be
12091
        // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer.
12092
        // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified.
12093
0
        bool keep_existing = false;
12094
0
        if (g.OpenPopupStack[current_stack_size].PopupId == id)
12095
0
            if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen))
12096
0
                keep_existing = true;
12097
0
        if (keep_existing)
12098
0
        {
12099
            // No reopen
12100
0
            g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;
12101
0
        }
12102
0
        else
12103
0
        {
12104
            // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation)
12105
0
            ClosePopupToLevel(current_stack_size, true);
12106
0
            g.OpenPopupStack.push_back(popup_ref);
12107
0
        }
12108
12109
        // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().
12110
        // This is equivalent to what ClosePopupToLevel() does.
12111
        //if (g.OpenPopupStack[current_stack_size].PopupId == id)
12112
        //    FocusWindow(parent_window);
12113
0
    }
12114
1
}
12115
12116
// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.
12117
// This function closes any popups that are over 'ref_window'.
12118
void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)
12119
8
{
12120
8
    ImGuiContext& g = *GImGui;
12121
8
    if (g.OpenPopupStack.Size == 0)
12122
7
        return;
12123
12124
    // Don't close our own child popup windows.
12125
    //IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\") restore_under=%d\n", ref_window ? ref_window->Name : "<NULL>", restore_focus_to_window_under_popup);
12126
1
    int popup_count_to_keep = 0;
12127
1
    if (ref_window)
12128
1
    {
12129
        // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)
12130
2
        for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)
12131
1
        {
12132
1
            ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];
12133
1
            if (!popup.Window)
12134
0
                continue;
12135
1
            IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);
12136
12137
            // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)
12138
            // - Clicking/Focusing Window2 won't close Popup1:
12139
            //     Window -> Popup1 -> Window2(Ref)
12140
            // - Clicking/focusing Popup1 will close Popup2 and Popup3:
12141
            //     Window -> Popup1(Ref) -> Popup2 -> Popup3
12142
            // - Each popups may contain child windows, which is why we compare ->RootWindow!
12143
            //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child
12144
            // We step through every popup from bottom to top to validate their position relative to reference window.
12145
1
            bool ref_window_is_descendent_of_popup = false;
12146
1
            for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)
12147
1
                if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)
12148
1
                    if (IsWindowWithinBeginStackOf(ref_window, popup_window))
12149
1
                    {
12150
1
                        ref_window_is_descendent_of_popup = true;
12151
1
                        break;
12152
1
                    }
12153
1
            if (!ref_window_is_descendent_of_popup)
12154
0
                break;
12155
1
        }
12156
1
    }
12157
1
    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
12158
0
    {
12159
0
        IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupsOverWindow(\"%s\")\n", ref_window ? ref_window->Name : "<NULL>");
12160
0
        ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);
12161
0
    }
12162
1
}
12163
12164
void ImGui::ClosePopupsExceptModals()
12165
0
{
12166
0
    ImGuiContext& g = *GImGui;
12167
12168
0
    int popup_count_to_keep;
12169
0
    for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)
12170
0
    {
12171
0
        ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;
12172
0
        if (!window || (window->Flags & ImGuiWindowFlags_Modal))
12173
0
            break;
12174
0
    }
12175
0
    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below
12176
0
        ClosePopupToLevel(popup_count_to_keep, true);
12177
0
}
12178
12179
void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)
12180
1
{
12181
1
    ImGuiContext& g = *GImGui;
12182
1
    IMGUI_DEBUG_LOG_POPUP("[popup] ClosePopupToLevel(%d), restore_under=%d\n", remaining, restore_focus_to_window_under_popup);
12183
1
    IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);
12184
1
    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)
12185
0
        for (int n = remaining; n < g.OpenPopupStack.Size; n++)
12186
0
            IMGUI_DEBUG_LOG_POPUP("[popup] - Closing PopupID 0x%08X Window \"%s\"\n", g.OpenPopupStack[n].PopupId, g.OpenPopupStack[n].Window ? g.OpenPopupStack[n].Window->Name : NULL);
12187
12188
    // Trim open popup stack
12189
1
    ImGuiPopupData prev_popup = g.OpenPopupStack[remaining];
12190
1
    g.OpenPopupStack.resize(remaining);
12191
12192
    // Restore focus (unless popup window was not yet submitted, and didn't have a chance to take focus anyhow. See #7325 for an edge case)
12193
1
    if (restore_focus_to_window_under_popup && prev_popup.Window)
12194
1
    {
12195
1
        ImGuiWindow* popup_window = prev_popup.Window;
12196
1
        ImGuiWindow* focus_window = (popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : prev_popup.RestoreNavWindow;
12197
1
        if (focus_window && !focus_window->WasActive)
12198
0
            FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback
12199
1
        else
12200
1
            FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);
12201
1
    }
12202
1
}
12203
12204
// Close the popup we have begin-ed into.
12205
void ImGui::CloseCurrentPopup()
12206
1
{
12207
1
    ImGuiContext& g = *GImGui;
12208
1
    int popup_idx = g.BeginPopupStack.Size - 1;
12209
1
    if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)
12210
0
        return;
12211
12212
    // Closing a menu closes its top-most parent popup (unless a modal)
12213
1
    while (popup_idx > 0)
12214
0
    {
12215
0
        ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;
12216
0
        ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;
12217
0
        bool close_parent = false;
12218
0
        if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))
12219
0
            if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))
12220
0
                close_parent = true;
12221
0
        if (!close_parent)
12222
0
            break;
12223
0
        popup_idx--;
12224
0
    }
12225
1
    IMGUI_DEBUG_LOG_POPUP("[popup] CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx);
12226
1
    ClosePopupToLevel(popup_idx, true);
12227
12228
    // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.
12229
    // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.
12230
    // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.
12231
1
    if (ImGuiWindow* window = g.NavWindow)
12232
1
        window->DC.NavHideHighlightOneFrame = true;
12233
1
}
12234
12235
// Attention! BeginPopup() adds default flags when calling BeginPopupEx()!
12236
bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags)
12237
0
{
12238
0
    ImGuiContext& g = *GImGui;
12239
0
    if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12240
0
    {
12241
0
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12242
0
        return false;
12243
0
    }
12244
12245
0
    char name[20];
12246
0
    IM_ASSERT((extra_window_flags & ImGuiWindowFlags_ChildMenu) == 0); // Use BeginPopupMenuEx()
12247
0
    ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // No recycling, so we can close/open during the same frame
12248
12249
0
    bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
12250
0
    if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12251
0
        EndPopup();
12252
    //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12253
0
    return is_open;
12254
0
}
12255
12256
bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags)
12257
660
{
12258
660
    ImGuiContext& g = *GImGui;
12259
660
    if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12260
0
    {
12261
0
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12262
0
        return false;
12263
0
    }
12264
12265
660
    char name[128];
12266
660
    IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu);
12267
660
    ImFormatString(name, IM_ARRAYSIZE(name), "%s###Menu_%02d", label, g.BeginMenuDepth); // Recycle windows based on depth
12268
660
    bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup);
12269
660
    if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)
12270
0
        EndPopup();
12271
    //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack;
12272
660
    return is_open;
12273
660
}
12274
12275
bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
12276
0
{
12277
0
    ImGuiContext& g = *GImGui;
12278
0
    if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance
12279
0
    {
12280
0
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12281
0
        return false;
12282
0
    }
12283
0
    flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;
12284
0
    ImGuiID id = g.CurrentWindow->GetID(str_id);
12285
0
    return BeginPopupEx(id, flags);
12286
0
}
12287
12288
// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.
12289
// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).
12290
// - *p_open set back to false in BeginPopupModal() when popup is not open.
12291
// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.
12292
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
12293
0
{
12294
0
    ImGuiContext& g = *GImGui;
12295
0
    ImGuiWindow* window = g.CurrentWindow;
12296
0
    const ImGuiID id = window->GetID(name);
12297
0
    if (!IsPopupOpen(id, ImGuiPopupFlags_None))
12298
0
    {
12299
0
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
12300
0
        if (p_open && *p_open)
12301
0
            *p_open = false;
12302
0
        return false;
12303
0
    }
12304
12305
    // Center modal windows by default for increased visibility
12306
    // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)
12307
    // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.
12308
0
    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasPos) == 0)
12309
0
    {
12310
0
        const ImGuiViewport* viewport = GetMainViewport();
12311
0
        SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
12312
0
    }
12313
12314
0
    flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;
12315
0
    const bool is_open = Begin(name, p_open, flags);
12316
0
    if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
12317
0
    {
12318
0
        EndPopup();
12319
0
        if (is_open)
12320
0
            ClosePopupToLevel(g.BeginPopupStack.Size, true);
12321
0
        return false;
12322
0
    }
12323
0
    return is_open;
12324
0
}
12325
12326
void ImGui::EndPopup()
12327
660
{
12328
660
    ImGuiContext& g = *GImGui;
12329
660
    ImGuiWindow* window = g.CurrentWindow;
12330
660
    if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || g.BeginPopupStack.Size == 0)
12331
0
    {
12332
0
        IM_ASSERT_USER_ERROR(0, "Calling EndPopup() too many times or in wrong window!");
12333
0
        return;
12334
0
    }
12335
12336
    // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)
12337
660
    if (g.NavWindow == window)
12338
659
        NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);
12339
12340
    // Child-popups don't need to be laid out
12341
660
    const ImGuiID backup_within_end_child_id = g.WithinEndChildID;
12342
660
    if (window->Flags & ImGuiWindowFlags_ChildWindow)
12343
0
        g.WithinEndChildID = window->ID;
12344
660
    End();
12345
660
    g.WithinEndChildID = backup_within_end_child_id;
12346
660
}
12347
12348
// Helper to open a popup if mouse button is released over the item
12349
// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()
12350
void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)
12351
0
{
12352
0
    ImGuiContext& g = *GImGui;
12353
0
    ImGuiWindow* window = g.CurrentWindow;
12354
0
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12355
0
    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12356
0
    {
12357
0
        ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
12358
0
        IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12359
0
        OpenPopupEx(id, popup_flags);
12360
0
    }
12361
0
}
12362
12363
// This is a helper to handle the simplest case of associating one named popup to one given widget.
12364
// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.
12365
// - To create a popup with a specific identifier, pass it in str_id.
12366
//    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.
12367
//    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.
12368
// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).
12369
//   This is essentially the same as:
12370
//       id = str_id ? GetID(str_id) : GetItemID();
12371
//       OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);
12372
//       return BeginPopup(id);
12373
//   Which is essentially the same as:
12374
//       id = str_id ? GetID(str_id) : GetItemID();
12375
//       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
12376
//           OpenPopup(id);
12377
//       return BeginPopup(id);
12378
//   The main difference being that this is tweaked to avoid computing the ID twice.
12379
bool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)
12380
0
{
12381
0
    ImGuiContext& g = *GImGui;
12382
0
    ImGuiWindow* window = g.CurrentWindow;
12383
0
    if (window->SkipItems)
12384
0
        return false;
12385
0
    ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!
12386
0
    IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)
12387
0
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12388
0
    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12389
0
        OpenPopupEx(id, popup_flags);
12390
0
    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12391
0
}
12392
12393
bool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)
12394
0
{
12395
0
    ImGuiContext& g = *GImGui;
12396
0
    ImGuiWindow* window = g.CurrentWindow;
12397
0
    if (!str_id)
12398
0
        str_id = "window_context";
12399
0
    ImGuiID id = window->GetID(str_id);
12400
0
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12401
0
    if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))
12402
0
        if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())
12403
0
            OpenPopupEx(id, popup_flags);
12404
0
    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12405
0
}
12406
12407
bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)
12408
0
{
12409
0
    ImGuiContext& g = *GImGui;
12410
0
    ImGuiWindow* window = g.CurrentWindow;
12411
0
    if (!str_id)
12412
0
        str_id = "void_context";
12413
0
    ImGuiID id = window->GetID(str_id);
12414
0
    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);
12415
0
    if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))
12416
0
        if (GetTopMostPopupModal() == NULL)
12417
0
            OpenPopupEx(id, popup_flags);
12418
0
    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);
12419
0
}
12420
12421
// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)
12422
// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.
12423
// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor
12424
//  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.
12425
//  this allows us to have tooltips/popups displayed out of the parent viewport.)
12426
ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)
12427
660
{
12428
660
    ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);
12429
    //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));
12430
    //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));
12431
12432
    // Combo Box policy (we want a connecting edge)
12433
660
    if (policy == ImGuiPopupPositionPolicy_ComboBox)
12434
0
    {
12435
0
        const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };
12436
0
        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12437
0
        {
12438
0
            const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12439
0
            if (n != -1 && dir == *last_dir) // Already tried this direction?
12440
0
                continue;
12441
0
            ImVec2 pos;
12442
0
            if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)
12443
0
            if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right
12444
0
            if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left
12445
0
            if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left
12446
0
            if (!r_outer.Contains(ImRect(pos, pos + size)))
12447
0
                continue;
12448
0
            *last_dir = dir;
12449
0
            return pos;
12450
0
        }
12451
0
    }
12452
12453
    // Tooltip and Default popup policy
12454
    // (Always first try the direction we used on the last frame, if any)
12455
660
    if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)
12456
660
    {
12457
660
        const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };
12458
661
        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)
12459
661
        {
12460
661
            const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];
12461
661
            if (n != -1 && dir == *last_dir) // Already tried this direction?
12462
0
                continue;
12463
12464
661
            const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);
12465
661
            const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);
12466
12467
            // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)
12468
661
            if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))
12469
1
                continue;
12470
660
            if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))
12471
0
                continue;
12472
12473
660
            ImVec2 pos;
12474
660
            pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;
12475
660
            pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;
12476
12477
            // Clamp top-left corner of popup
12478
660
            pos.x = ImMax(pos.x, r_outer.Min.x);
12479
660
            pos.y = ImMax(pos.y, r_outer.Min.y);
12480
12481
660
            *last_dir = dir;
12482
660
            return pos;
12483
660
        }
12484
660
    }
12485
12486
    // Fallback when not enough room:
12487
0
    *last_dir = ImGuiDir_None;
12488
12489
    // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
12490
0
    if (policy == ImGuiPopupPositionPolicy_Tooltip)
12491
0
        return ref_pos + ImVec2(2, 2);
12492
12493
    // Otherwise try to keep within display
12494
0
    ImVec2 pos = ref_pos;
12495
0
    pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);
12496
0
    pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);
12497
0
    return pos;
12498
0
}
12499
12500
// Note that this is used for popups, which can overlap the non work-area of individual viewports.
12501
ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)
12502
660
{
12503
660
    ImGuiContext& g = *GImGui;
12504
660
    IM_UNUSED(window);
12505
660
    ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();
12506
660
    ImVec2 padding = g.Style.DisplaySafeAreaPadding;
12507
660
    r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));
12508
660
    return r_screen;
12509
660
}
12510
12511
ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
12512
660
{
12513
660
    ImGuiContext& g = *GImGui;
12514
12515
660
    ImRect r_outer = GetPopupAllowedExtentRect(window);
12516
660
    if (window->Flags & ImGuiWindowFlags_ChildMenu)
12517
660
    {
12518
        // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.
12519
        // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.
12520
660
        IM_ASSERT(g.CurrentWindow == window);
12521
660
        ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;
12522
660
        float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).
12523
660
        ImRect r_avoid;
12524
660
        if (parent_window->DC.MenuBarAppending)
12525
660
            r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field
12526
0
        else
12527
0
            r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);
12528
660
        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);
12529
660
    }
12530
0
    if (window->Flags & ImGuiWindowFlags_Popup)
12531
0
    {
12532
0
        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here
12533
0
    }
12534
0
    if (window->Flags & ImGuiWindowFlags_Tooltip)
12535
0
    {
12536
        // Position tooltip (always follows mouse + clamp within outer boundaries)
12537
        // FIXME:
12538
        // - Too many paths. One problem is that FindBestWindowPosForPopupEx() doesn't allow passing a suggested position (so touch screen path doesn't use it by default).
12539
        // - Drag and drop tooltips are not using this path either: BeginTooltipEx() manually sets their position.
12540
        // - Require some tidying up. In theory we could handle both cases in same location, but requires a bit of shuffling
12541
        //   as drag and drop tooltips are calling SetNextWindowPos() leading to 'window_pos_set_by_api' being set in Begin().
12542
0
        IM_ASSERT(g.CurrentWindow == window);
12543
0
        const float scale = g.Style.MouseCursorScale;
12544
0
        const ImVec2 ref_pos = NavCalcPreferredRefPos();
12545
12546
0
        if (g.IO.MouseSource == ImGuiMouseSource_TouchScreen && NavCalcPreferredRefPosSource() == ImGuiInputSource_Mouse)
12547
0
        {
12548
0
            ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_TOUCH * scale - (TOOLTIP_DEFAULT_PIVOT_TOUCH * window->Size);
12549
0
            if (r_outer.Contains(ImRect(tooltip_pos, tooltip_pos + window->Size)))
12550
0
                return tooltip_pos;
12551
0
        }
12552
12553
0
        ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET_MOUSE * scale;
12554
0
        ImRect r_avoid;
12555
0
        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && !g.IO.ConfigNavMoveSetMousePos)
12556
0
            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
12557
0
        else
12558
0
            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
12559
        //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
12560
12561
0
        return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
12562
0
    }
12563
0
    IM_ASSERT(0);
12564
0
    return window->Pos;
12565
0
}
12566
12567
//-----------------------------------------------------------------------------
12568
// [SECTION] WINDOW FOCUS
12569
//----------------------------------------------------------------------------
12570
// - SetWindowFocus()
12571
// - SetNextWindowFocus()
12572
// - IsWindowFocused()
12573
// - UpdateWindowInFocusOrderList() [Internal]
12574
// - BringWindowToFocusFront() [Internal]
12575
// - BringWindowToDisplayFront() [Internal]
12576
// - BringWindowToDisplayBack() [Internal]
12577
// - BringWindowToDisplayBehind() [Internal]
12578
// - FindWindowDisplayIndex() [Internal]
12579
// - FocusWindow() [Internal]
12580
// - FocusTopMostWindowUnderOne() [Internal]
12581
//-----------------------------------------------------------------------------
12582
12583
void ImGui::SetWindowFocus()
12584
0
{
12585
0
    FocusWindow(GImGui->CurrentWindow);
12586
0
}
12587
12588
void ImGui::SetWindowFocus(const char* name)
12589
0
{
12590
0
    if (name)
12591
0
    {
12592
0
        if (ImGuiWindow* window = FindWindowByName(name))
12593
0
            FocusWindow(window);
12594
0
    }
12595
0
    else
12596
0
    {
12597
0
        FocusWindow(NULL);
12598
0
    }
12599
0
}
12600
12601
void ImGui::SetNextWindowFocus()
12602
0
{
12603
0
    ImGuiContext& g = *GImGui;
12604
0
    g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasFocus;
12605
0
}
12606
12607
// Similar to IsWindowHovered()
12608
bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
12609
0
{
12610
0
    ImGuiContext& g = *GImGui;
12611
0
    ImGuiWindow* ref_window = g.NavWindow;
12612
0
    ImGuiWindow* cur_window = g.CurrentWindow;
12613
12614
0
    if (ref_window == NULL)
12615
0
        return false;
12616
0
    if (flags & ImGuiFocusedFlags_AnyWindow)
12617
0
        return true;
12618
12619
0
    IM_ASSERT(cur_window); // Not inside a Begin()/End()
12620
0
    const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
12621
0
    if (flags & ImGuiFocusedFlags_RootWindow)
12622
0
        cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);
12623
12624
0
    if (flags & ImGuiFocusedFlags_ChildWindows)
12625
0
        return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);
12626
0
    else
12627
0
        return (ref_window == cur_window);
12628
0
}
12629
12630
static int ImGui::FindWindowFocusIndex(ImGuiWindow* window)
12631
3
{
12632
3
    ImGuiContext& g = *GImGui;
12633
3
    IM_UNUSED(g);
12634
3
    int order = window->FocusOrder;
12635
3
    IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)
12636
3
    IM_ASSERT(g.WindowsFocusOrder[order] == window);
12637
3
    return order;
12638
3
}
12639
12640
static void ImGui::UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)
12641
161k
{
12642
161k
    ImGuiContext& g = *GImGui;
12643
12644
161k
    const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);
12645
161k
    const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;
12646
161k
    if ((just_created || child_flag_changed) && !new_is_explicit_child)
12647
3
    {
12648
3
        IM_ASSERT(!g.WindowsFocusOrder.contains(window));
12649
3
        g.WindowsFocusOrder.push_back(window);
12650
3
        window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);
12651
3
    }
12652
161k
    else if (!just_created && child_flag_changed && new_is_explicit_child)
12653
0
    {
12654
0
        IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);
12655
0
        for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)
12656
0
            g.WindowsFocusOrder[n]->FocusOrder--;
12657
0
        g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);
12658
0
        window->FocusOrder = -1;
12659
0
    }
12660
161k
    window->IsExplicitChild = new_is_explicit_child;
12661
161k
}
12662
12663
void ImGui::BringWindowToFocusFront(ImGuiWindow* window)
12664
7
{
12665
7
    ImGuiContext& g = *GImGui;
12666
7
    IM_ASSERT(window == window->RootWindow);
12667
12668
7
    const int cur_order = window->FocusOrder;
12669
7
    IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);
12670
7
    if (g.WindowsFocusOrder.back() == window)
12671
4
        return;
12672
12673
3
    const int new_order = g.WindowsFocusOrder.Size - 1;
12674
6
    for (int n = cur_order; n < new_order; n++)
12675
3
    {
12676
3
        g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];
12677
3
        g.WindowsFocusOrder[n]->FocusOrder--;
12678
3
        IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);
12679
3
    }
12680
3
    g.WindowsFocusOrder[new_order] = window;
12681
3
    window->FocusOrder = (short)new_order;
12682
3
}
12683
12684
// Note technically focus related but rather adjacent and close to BringWindowToFocusFront()
12685
void ImGui::BringWindowToDisplayFront(ImGuiWindow* window)
12686
7
{
12687
7
    ImGuiContext& g = *GImGui;
12688
7
    ImGuiWindow* current_front_window = g.Windows.back();
12689
7
    if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)
12690
4
        return;
12691
3
    for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window
12692
3
        if (g.Windows[i] == window)
12693
3
        {
12694
3
            memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));
12695
3
            g.Windows[g.Windows.Size - 1] = window;
12696
3
            break;
12697
3
        }
12698
3
}
12699
12700
void ImGui::BringWindowToDisplayBack(ImGuiWindow* window)
12701
0
{
12702
0
    ImGuiContext& g = *GImGui;
12703
0
    if (g.Windows[0] == window)
12704
0
        return;
12705
0
    for (int i = 0; i < g.Windows.Size; i++)
12706
0
        if (g.Windows[i] == window)
12707
0
        {
12708
0
            memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));
12709
0
            g.Windows[0] = window;
12710
0
            break;
12711
0
        }
12712
0
}
12713
12714
void ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)
12715
0
{
12716
0
    IM_ASSERT(window != NULL && behind_window != NULL);
12717
0
    ImGuiContext& g = *GImGui;
12718
0
    window = window->RootWindow;
12719
0
    behind_window = behind_window->RootWindow;
12720
0
    int pos_wnd = FindWindowDisplayIndex(window);
12721
0
    int pos_beh = FindWindowDisplayIndex(behind_window);
12722
0
    if (pos_wnd < pos_beh)
12723
0
    {
12724
0
        size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);
12725
0
        memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);
12726
0
        g.Windows[pos_beh - 1] = window;
12727
0
    }
12728
0
    else
12729
0
    {
12730
0
        size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);
12731
0
        memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);
12732
0
        g.Windows[pos_beh] = window;
12733
0
    }
12734
0
}
12735
12736
int ImGui::FindWindowDisplayIndex(ImGuiWindow* window)
12737
0
{
12738
0
    ImGuiContext& g = *GImGui;
12739
0
    return g.Windows.index_from_ptr(g.Windows.find(window));
12740
0
}
12741
12742
// Moving window to front of display and set focus (which happens to be back of our sorted list)
12743
void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)
12744
9
{
12745
9
    ImGuiContext& g = *GImGui;
12746
12747
    // Modal check?
12748
9
    if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.
12749
6
        if (ImGuiWindow* blocking_modal = FindBlockingModal(window))
12750
0
        {
12751
            // This block would typically be reached in two situations:
12752
            // - API call to FocusWindow() with a window under a modal and ImGuiFocusRequestFlags_UnlessBelowModal flag.
12753
            // - User clicking on void or anything behind a modal while a modal is open (window == NULL)
12754
0
            IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "<NULL>", blocking_modal->Name);
12755
0
            if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
12756
0
                BringWindowToDisplayBehind(window, blocking_modal); // Still bring right under modal. (FIXME: Could move in focus list too?)
12757
0
            ClosePopupsOverWindow(GetTopMostPopupModal(), false); // Note how we need to use GetTopMostPopupModal() aad NOT blocking_modal, to handle nested modals
12758
0
            return;
12759
0
        }
12760
12761
    // Find last focused child (if any) and focus it instead.
12762
9
    if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)
12763
3
        window = NavRestoreLastChildNavWindow(window);
12764
12765
    // Apply focus
12766
9
    if (g.NavWindow != window)
12767
8
    {
12768
8
        SetNavWindow(window);
12769
8
        if (window && g.NavHighlightItemUnderNav)
12770
0
            g.NavMousePosDirty = true;
12771
8
        g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
12772
8
        g.NavLayer = ImGuiNavLayer_Main;
12773
8
        SetNavFocusScope(window ? window->NavRootFocusScopeId : 0);
12774
8
        g.NavIdIsAlive = false;
12775
8
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12776
12777
        // Close popups if any
12778
8
        ClosePopupsOverWindow(window, false);
12779
8
    }
12780
12781
    // Move the root window to the top of the pile
12782
9
    IM_ASSERT(window == NULL || window->RootWindow != NULL);
12783
9
    ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop
12784
9
    ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;
12785
12786
    // Steal active widgets. Some of the cases it triggers includes:
12787
    // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.
12788
    // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)
12789
9
    if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)
12790
0
        if (!g.ActiveIdNoClearOnFocusLoss)
12791
0
            ClearActiveID();
12792
12793
    // Passing NULL allow to disable keyboard focus
12794
9
    if (!window)
12795
2
        return;
12796
12797
    // Bring to front
12798
7
    BringWindowToFocusFront(focus_front_window);
12799
7
    if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)
12800
7
        BringWindowToDisplayFront(display_front_window);
12801
7
}
12802
12803
void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)
12804
4
{
12805
4
    ImGuiContext& g = *GImGui;
12806
4
    IM_UNUSED(filter_viewport); // Unused in master branch.
12807
4
    int start_idx = g.WindowsFocusOrder.Size - 1;
12808
4
    if (under_this_window != NULL)
12809
3
    {
12810
        // Aim at root window behind us, if we are in a child window that's our own root (see #4640)
12811
3
        int offset = -1;
12812
3
        while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)
12813
0
        {
12814
0
            under_this_window = under_this_window->ParentWindow;
12815
0
            offset = 0;
12816
0
        }
12817
3
        start_idx = FindWindowFocusIndex(under_this_window) + offset;
12818
3
    }
12819
8
    for (int i = start_idx; i >= 0; i--)
12820
6
    {
12821
        // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.
12822
6
        ImGuiWindow* window = g.WindowsFocusOrder[i];
12823
6
        if (window == ignore_window || !window->WasActive)
12824
4
            continue;
12825
2
        if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))
12826
2
        {
12827
2
            FocusWindow(window, flags);
12828
2
            return;
12829
2
        }
12830
2
    }
12831
2
    FocusWindow(NULL, flags);
12832
2
}
12833
12834
//-----------------------------------------------------------------------------
12835
// [SECTION] KEYBOARD/GAMEPAD NAVIGATION
12836
//-----------------------------------------------------------------------------
12837
12838
// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.
12839
// In our terminology those should be interchangeable, yet right now this is super confusing.
12840
// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.
12841
12842
void ImGui::SetNavCursorVisible(bool visible)
12843
0
{
12844
0
    ImGuiContext& g = *GImGui;
12845
0
    if (g.IO.ConfigNavCursorVisibleAlways)
12846
0
        visible = true;
12847
0
    g.NavCursorVisible = visible;
12848
0
}
12849
12850
// (was called NavRestoreHighlightAfterMove() before 1.91.4)
12851
void ImGui::SetNavCursorVisibleAfterMove()
12852
0
{
12853
0
    ImGuiContext& g = *GImGui;
12854
0
    if (g.IO.ConfigNavCursorVisibleAuto)
12855
0
        g.NavCursorVisible = true;
12856
0
    g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
12857
0
}
12858
12859
void ImGui::SetNavWindow(ImGuiWindow* window)
12860
9
{
12861
9
    ImGuiContext& g = *GImGui;
12862
9
    if (g.NavWindow != window)
12863
9
    {
12864
9
        IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : "<NULL>");
12865
9
        g.NavWindow = window;
12866
9
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
12867
9
    }
12868
9
    g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;
12869
9
    NavUpdateAnyRequestFlag();
12870
9
}
12871
12872
void ImGui::NavHighlightActivated(ImGuiID id)
12873
0
{
12874
0
    ImGuiContext& g = *GImGui;
12875
0
    g.NavHighlightActivatedId = id;
12876
0
    g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER;
12877
0
}
12878
12879
void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)
12880
824
{
12881
824
    ImGuiContext& g = *GImGui;
12882
824
    g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;
12883
824
}
12884
12885
void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)
12886
410
{
12887
410
    ImGuiContext& g = *GImGui;
12888
410
    IM_ASSERT(g.NavWindow != NULL);
12889
410
    IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);
12890
410
    g.NavId = id;
12891
410
    g.NavLayer = nav_layer;
12892
410
    SetNavFocusScope(focus_scope_id);
12893
410
    g.NavWindow->NavLastIds[nav_layer] = id;
12894
410
    g.NavWindow->NavRectRel[nav_layer] = rect_rel;
12895
12896
    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12897
410
    NavClearPreferredPosForAxis(ImGuiAxis_X);
12898
410
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
12899
410
}
12900
12901
void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)
12902
2
{
12903
2
    ImGuiContext& g = *GImGui;
12904
2
    IM_ASSERT(id != 0);
12905
12906
2
    if (g.NavWindow != window)
12907
1
       SetNavWindow(window);
12908
12909
    // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.
12910
    // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)
12911
2
    const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;
12912
2
    g.NavId = id;
12913
2
    g.NavLayer = nav_layer;
12914
2
    SetNavFocusScope(g.CurrentFocusScopeId);
12915
2
    window->NavLastIds[nav_layer] = id;
12916
2
    if (g.LastItemData.ID == id)
12917
2
        window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);
12918
12919
2
    if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
12920
0
        g.NavHighlightItemUnderNav = true;
12921
2
    else if (g.IO.ConfigNavCursorVisibleAuto)
12922
2
        g.NavCursorVisible = false;
12923
12924
    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)
12925
2
    NavClearPreferredPosForAxis(ImGuiAxis_X);
12926
2
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
12927
2
}
12928
12929
static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)
12930
0
{
12931
0
    if (ImFabs(dx) > ImFabs(dy))
12932
0
        return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;
12933
0
    return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;
12934
0
}
12935
12936
static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)
12937
0
{
12938
0
    if (cand_max < curr_min)
12939
0
        return cand_max - curr_min;
12940
0
    if (curr_max < cand_min)
12941
0
        return cand_min - curr_max;
12942
0
    return 0.0f;
12943
0
}
12944
12945
// Scoring function for keyboard/gamepad directional navigation. Based on https://gist.github.com/rygorous/6981057
12946
static bool ImGui::NavScoreItem(ImGuiNavItemData* result, const ImRect& nav_bb)
12947
0
{
12948
0
    ImGuiContext& g = *GImGui;
12949
0
    ImGuiWindow* window = g.CurrentWindow;
12950
0
    if (g.NavLayer != window->DC.NavLayerCurrent)
12951
0
        return false;
12952
12953
    // FIXME: Those are not good variables names
12954
0
    ImRect cand = nav_bb;                   // Current item nav rectangle
12955
0
    const ImRect curr = g.NavScoringRect;   // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)
12956
0
    g.NavScoringDebugCount++;
12957
12958
    // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring
12959
0
    if (window->ParentWindow == g.NavWindow)
12960
0
    {
12961
0
        IM_ASSERT((window->ChildFlags | g.NavWindow->ChildFlags) & ImGuiChildFlags_NavFlattened);
12962
0
        if (!window->ClipRect.Overlaps(cand))
12963
0
            return false;
12964
0
        cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window
12965
0
    }
12966
12967
    // Compute distance between boxes
12968
    // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.
12969
0
    float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);
12970
0
    float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items
12971
0
    if (dby != 0.0f && dbx != 0.0f)
12972
0
        dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);
12973
0
    float dist_box = ImFabs(dbx) + ImFabs(dby);
12974
12975
    // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)
12976
0
    float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);
12977
0
    float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);
12978
0
    float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)
12979
12980
    // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance
12981
0
    ImGuiDir quadrant;
12982
0
    float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;
12983
0
    if (dbx != 0.0f || dby != 0.0f)
12984
0
    {
12985
        // For non-overlapping boxes, use distance between boxes
12986
        // FIXME-NAV: Quadrant may be incorrect because of (1) dbx bias and (2) curr.Max.y bias applied by NavBiasScoringRect() where typically curr.Max.y==curr.Min.y
12987
        // One typical case where this happens, with style.WindowMenuButtonPosition == ImGuiDir_Right, pressing Left to navigate from Close to Collapse tends to fail.
12988
        // Also see #6344. Calling ImGetDirQuadrantFromDelta() with unbiased values may be good but side-effects are plenty.
12989
0
        dax = dbx;
12990
0
        day = dby;
12991
0
        dist_axial = dist_box;
12992
0
        quadrant = ImGetDirQuadrantFromDelta(dbx, dby);
12993
0
    }
12994
0
    else if (dcx != 0.0f || dcy != 0.0f)
12995
0
    {
12996
        // For overlapping boxes with different centers, use distance between centers
12997
0
        dax = dcx;
12998
0
        day = dcy;
12999
0
        dist_axial = dist_center;
13000
0
        quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);
13001
0
    }
13002
0
    else
13003
0
    {
13004
        // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)
13005
0
        quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;
13006
0
    }
13007
13008
0
    const ImGuiDir move_dir = g.NavMoveDir;
13009
#if IMGUI_DEBUG_NAV_SCORING
13010
    char buf[200];
13011
    if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.
13012
    {
13013
        if (quadrant == move_dir)
13014
        {
13015
            ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center);
13016
            ImDrawList* draw_list = GetForegroundDrawList(window);
13017
            draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));
13018
            draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));
13019
            draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);
13020
        }
13021
    }
13022
    const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);
13023
    const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));
13024
    if (debug_hovering || debug_tty)
13025
    {
13026
        ImFormatString(buf, IM_ARRAYSIZE(buf),
13027
            "d-box    (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial  (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c",
13028
            dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]);
13029
        if (debug_hovering)
13030
        {
13031
            ImDrawList* draw_list = GetForegroundDrawList(window);
13032
            draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));
13033
            draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));
13034
            draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));
13035
            draw_list->AddText(cand.Max, ~0U, buf);
13036
        }
13037
        if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); }
13038
    }
13039
#endif
13040
13041
    // Is it in the quadrant we're interested in moving to?
13042
0
    bool new_best = false;
13043
0
    if (quadrant == move_dir)
13044
0
    {
13045
        // Does it beat the current best candidate?
13046
0
        if (dist_box < result->DistBox)
13047
0
        {
13048
0
            result->DistBox = dist_box;
13049
0
            result->DistCenter = dist_center;
13050
0
            return true;
13051
0
        }
13052
0
        if (dist_box == result->DistBox)
13053
0
        {
13054
            // Try using distance between center points to break ties
13055
0
            if (dist_center < result->DistCenter)
13056
0
            {
13057
0
                result->DistCenter = dist_center;
13058
0
                new_best = true;
13059
0
            }
13060
0
            else if (dist_center == result->DistCenter)
13061
0
            {
13062
                // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items
13063
                // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index),
13064
                // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.
13065
0
                if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance
13066
0
                    new_best = true;
13067
0
            }
13068
0
        }
13069
0
    }
13070
13071
    // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches
13072
    // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)
13073
    // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.
13074
    // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.
13075
    // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?
13076
0
    if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match
13077
0
        if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
13078
0
            if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))
13079
0
            {
13080
0
                result->DistAxial = dist_axial;
13081
0
                new_best = true;
13082
0
            }
13083
13084
0
    return new_best;
13085
0
}
13086
13087
static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)
13088
1
{
13089
1
    ImGuiContext& g = *GImGui;
13090
1
    ImGuiWindow* window = g.CurrentWindow;
13091
1
    result->Window = window;
13092
1
    result->ID = g.LastItemData.ID;
13093
1
    result->FocusScopeId = g.CurrentFocusScopeId;
13094
1
    result->ItemFlags = g.LastItemData.ItemFlags;
13095
1
    result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);
13096
1
    if (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13097
0
    {
13098
0
        IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13099
0
        result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13100
0
    }
13101
1
}
13102
13103
// True when current work location may be scrolled horizontally when moving left / right.
13104
// This is generally always true UNLESS within a column. We don't have a vertical equivalent.
13105
void ImGui::NavUpdateCurrentWindowIsScrollPushableX()
13106
243k
{
13107
243k
    ImGuiContext& g = *GImGui;
13108
243k
    ImGuiWindow* window = g.CurrentWindow;
13109
243k
    window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);
13110
243k
}
13111
13112
// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)
13113
// This is called after LastItemData is set, but NextItemData is also still valid.
13114
static void ImGui::NavProcessItem()
13115
664
{
13116
664
    ImGuiContext& g = *GImGui;
13117
664
    ImGuiWindow* window = g.CurrentWindow;
13118
664
    const ImGuiID id = g.LastItemData.ID;
13119
664
    const ImGuiItemFlags item_flags = g.LastItemData.ItemFlags;
13120
13121
    // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221, #8816)
13122
664
    ImRect nav_bb = g.LastItemData.NavRect;
13123
664
    if (window->DC.NavIsScrollPushableX == false)
13124
0
    {
13125
0
        nav_bb.Min.x = ImClamp(nav_bb.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13126
0
        nav_bb.Max.x = ImClamp(nav_bb.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);
13127
0
    }
13128
13129
    // Process Init Request
13130
664
    if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)
13131
1
    {
13132
        // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback
13133
1
        const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;
13134
1
        if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)
13135
1
        {
13136
1
            NavApplyItemToResult(&g.NavInitResult);
13137
1
        }
13138
1
        if (candidate_for_nav_default_focus)
13139
1
        {
13140
1
            g.NavInitRequest = false; // Found a match, clear request
13141
1
            NavUpdateAnyRequestFlag();
13142
1
        }
13143
1
    }
13144
13145
    // Process Move Request (scoring for navigation)
13146
    // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)
13147
664
    if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)
13148
0
    {
13149
0
        if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0)
13150
0
        {
13151
0
            const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13152
0
            if (is_tabbing)
13153
0
            {
13154
0
                NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);
13155
0
            }
13156
0
            else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))
13157
0
            {
13158
0
                ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;
13159
0
                if (NavScoreItem(result, nav_bb))
13160
0
                    NavApplyItemToResult(result);
13161
13162
                // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.
13163
0
                const float VISIBLE_RATIO = 0.70f;
13164
0
                if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))
13165
0
                    if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)
13166
0
                        if (NavScoreItem(&g.NavMoveResultLocalVisible, nav_bb))
13167
0
                            NavApplyItemToResult(&g.NavMoveResultLocalVisible);
13168
0
            }
13169
0
        }
13170
0
    }
13171
13172
    // Update information for currently focused/navigated item
13173
664
    if (g.NavId == id)
13174
659
    {
13175
659
        if (g.NavWindow != window)
13176
0
            SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.
13177
659
        g.NavLayer = window->DC.NavLayerCurrent;
13178
659
        SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath
13179
659
        g.NavFocusScopeId = g.CurrentFocusScopeId;
13180
659
        g.NavIdIsAlive = true;
13181
659
        if (g.LastItemData.ItemFlags & ImGuiItemFlags_HasSelectionUserData)
13182
0
        {
13183
0
            IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);
13184
0
            g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.
13185
0
        }
13186
659
        window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)
13187
659
    }
13188
664
}
13189
13190
// Handle "scoring" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().
13191
// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!
13192
// - Case 1: no nav/active id:    set result to first eligible item, stop storing.
13193
// - Case 2: tab forward:         on ref id set counter, on counter elapse store result
13194
// - Case 3: tab forward wrap:    set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request
13195
// - Case 4: tab backward:        store all results, on ref id pick prev, stop storing
13196
// - Case 5: tab backward wrap:   store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested
13197
void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)
13198
0
{
13199
0
    ImGuiContext& g = *GImGui;
13200
13201
0
    if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)
13202
0
    {
13203
0
        if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)
13204
0
            return;
13205
0
        if (g.NavFocusScopeId != g.CurrentFocusScopeId)
13206
0
            return;
13207
0
    }
13208
13209
    // - Can always land on an item when using API call.
13210
    // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.
13211
    // - Tabbing without _NavEnableKeyboard: goes through inputable items only.
13212
0
    bool can_stop;
13213
0
    if (move_flags & ImGuiNavMoveFlags_FocusApi)
13214
0
        can_stop = true;
13215
0
    else
13216
0
        can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));
13217
13218
    // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)
13219
0
    ImGuiNavItemData* result = &g.NavMoveResultLocal;
13220
0
    if (g.NavTabbingDir == +1)
13221
0
    {
13222
        // Tab Forward or SetKeyboardFocusHere() with >= 0
13223
0
        if (can_stop && g.NavTabbingResultFirst.ID == 0)
13224
0
            NavApplyItemToResult(&g.NavTabbingResultFirst);
13225
0
        if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)
13226
0
            NavMoveRequestResolveWithLastItem(result);
13227
0
        else if (g.NavId == id)
13228
0
            g.NavTabbingCounter = 1;
13229
0
    }
13230
0
    else if (g.NavTabbingDir == -1)
13231
0
    {
13232
        // Tab Backward
13233
0
        if (g.NavId == id)
13234
0
        {
13235
0
            if (result->ID)
13236
0
            {
13237
0
                g.NavMoveScoringItems = false;
13238
0
                NavUpdateAnyRequestFlag();
13239
0
            }
13240
0
        }
13241
0
        else if (can_stop)
13242
0
        {
13243
            // Keep applying until reaching NavId
13244
0
            NavApplyItemToResult(result);
13245
0
        }
13246
0
    }
13247
0
    else if (g.NavTabbingDir == 0)
13248
0
    {
13249
0
        if (can_stop && g.NavId == id)
13250
0
            NavMoveRequestResolveWithLastItem(result);
13251
0
        if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init
13252
0
            NavApplyItemToResult(&g.NavTabbingResultFirst);
13253
0
    }
13254
0
}
13255
13256
bool ImGui::NavMoveRequestButNoResultYet()
13257
81.2k
{
13258
81.2k
    ImGuiContext& g = *GImGui;
13259
81.2k
    return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;
13260
81.2k
}
13261
13262
// FIXME: ScoringRect is not set
13263
void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13264
0
{
13265
0
    ImGuiContext& g = *GImGui;
13266
0
    IM_ASSERT(g.NavWindow != NULL);
13267
    //IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestSubmit: dir %c, window \"%s\"\n", "-WENS"[move_dir + 1], g.NavWindow->Name);
13268
13269
0
    if (move_flags & ImGuiNavMoveFlags_IsTabbing)
13270
0
        move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;
13271
13272
0
    g.NavMoveSubmitted = g.NavMoveScoringItems = true;
13273
0
    g.NavMoveDir = move_dir;
13274
0
    g.NavMoveDirForDebug = move_dir;
13275
0
    g.NavMoveClipDir = clip_dir;
13276
0
    g.NavMoveFlags = move_flags;
13277
0
    g.NavMoveScrollFlags = scroll_flags;
13278
0
    g.NavMoveForwardToNextFrame = false;
13279
0
    g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;
13280
0
    g.NavMoveResultLocal.Clear();
13281
0
    g.NavMoveResultLocalVisible.Clear();
13282
0
    g.NavMoveResultOther.Clear();
13283
0
    g.NavTabbingCounter = 0;
13284
0
    g.NavTabbingResultFirst.Clear();
13285
0
    NavUpdateAnyRequestFlag();
13286
0
}
13287
13288
void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)
13289
0
{
13290
0
    ImGuiContext& g = *GImGui;
13291
0
    g.NavMoveScoringItems = false; // Ensure request doesn't need more processing
13292
0
    NavApplyItemToResult(result);
13293
0
    NavUpdateAnyRequestFlag();
13294
0
}
13295
13296
// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsToParent
13297
void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data)
13298
0
{
13299
0
    ImGuiContext& g = *GImGui;
13300
0
    g.NavMoveScoringItems = false;
13301
0
    g.LastItemData.ID = tree_node_data->ID;
13302
0
    g.LastItemData.ItemFlags = tree_node_data->ItemFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).
13303
0
    g.LastItemData.NavRect = tree_node_data->NavRect;
13304
0
    NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()
13305
0
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
13306
0
    NavUpdateAnyRequestFlag();
13307
0
}
13308
13309
void ImGui::NavMoveRequestCancel()
13310
0
{
13311
0
    ImGuiContext& g = *GImGui;
13312
0
    g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13313
0
    NavUpdateAnyRequestFlag();
13314
0
}
13315
13316
// Forward will reuse the move request again on the next frame (generally with modifications done to it)
13317
void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)
13318
0
{
13319
0
    ImGuiContext& g = *GImGui;
13320
0
    IM_ASSERT(g.NavMoveForwardToNextFrame == false);
13321
0
    NavMoveRequestCancel();
13322
0
    g.NavMoveForwardToNextFrame = true;
13323
0
    g.NavMoveDir = move_dir;
13324
0
    g.NavMoveClipDir = clip_dir;
13325
0
    g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;
13326
0
    g.NavMoveScrollFlags = scroll_flags;
13327
0
}
13328
13329
// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire
13330
// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.
13331
void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)
13332
659
{
13333
659
    ImGuiContext& g = *GImGui;
13334
659
    IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY
13335
13336
    // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:
13337
    // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().
13338
659
    if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)
13339
0
        g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;
13340
659
}
13341
13342
// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).
13343
// This way we could find the last focused window among our children. It would be much less confusing this way?
13344
static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)
13345
662
{
13346
662
    ImGuiWindow* parent = nav_window;
13347
662
    while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
13348
0
        parent = parent->ParentWindow;
13349
662
    if (parent && parent != nav_window)
13350
0
        parent->NavLastChildNavWindow = nav_window;
13351
662
}
13352
13353
// Restore the last focused child.
13354
// Call when we are expected to land on the Main Layer (0) after FocusWindow()
13355
static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)
13356
3
{
13357
3
    if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)
13358
0
        return window->NavLastChildNavWindow;
13359
3
    return window;
13360
3
}
13361
13362
void ImGui::NavRestoreLayer(ImGuiNavLayer layer)
13363
0
{
13364
0
    ImGuiContext& g = *GImGui;
13365
0
    if (layer == ImGuiNavLayer_Main)
13366
0
    {
13367
0
        ImGuiWindow* prev_nav_window = g.NavWindow;
13368
0
        g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);    // FIXME-NAV: Should clear ongoing nav requests?
13369
0
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13370
0
        if (prev_nav_window)
13371
0
            IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name);
13372
0
    }
13373
0
    ImGuiWindow* window = g.NavWindow;
13374
0
    if (window->NavLastIds[layer] != 0)
13375
0
    {
13376
0
        SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
13377
0
    }
13378
0
    else
13379
0
    {
13380
0
        g.NavLayer = layer;
13381
0
        NavInitWindow(window, true);
13382
0
    }
13383
0
}
13384
13385
static inline void ImGui::NavUpdateAnyRequestFlag()
13386
80.6k
{
13387
80.6k
    ImGuiContext& g = *GImGui;
13388
80.6k
    g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);
13389
80.6k
    if (g.NavAnyRequest)
13390
80.6k
        IM_ASSERT(g.NavWindow != NULL);
13391
80.6k
}
13392
13393
// This needs to be called before we submit any widget (aka in or before Begin)
13394
void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)
13395
3
{
13396
3
    ImGuiContext& g = *GImGui;
13397
3
    IM_ASSERT(window == g.NavWindow);
13398
13399
3
    if (window->Flags & ImGuiWindowFlags_NoNavInputs)
13400
0
    {
13401
0
        g.NavId = 0;
13402
0
        SetNavFocusScope(window->NavRootFocusScopeId);
13403
0
        return;
13404
0
    }
13405
13406
3
    bool init_for_nav = false;
13407
3
    if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)
13408
3
        init_for_nav = true;
13409
3
    IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer);
13410
3
    if (init_for_nav)
13411
3
    {
13412
3
        SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());
13413
3
        g.NavInitRequest = true;
13414
3
        g.NavInitRequestFromMove = false;
13415
3
        g.NavInitResult.ID = 0;
13416
3
        NavUpdateAnyRequestFlag();
13417
3
    }
13418
0
    else
13419
0
    {
13420
0
        g.NavId = window->NavLastIds[0];
13421
0
        SetNavFocusScope(window->NavRootFocusScopeId);
13422
0
    }
13423
3
}
13424
13425
static ImGuiInputSource ImGui::NavCalcPreferredRefPosSource()
13426
1
{
13427
1
    ImGuiContext& g = *GImGui;
13428
1
    ImGuiWindow* window = g.NavWindow;
13429
1
    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13430
13431
    // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
13432
1
    if ((!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !window) && !activated_shortcut)
13433
1
        return ImGuiInputSource_Mouse;
13434
0
    else
13435
0
        return ImGuiInputSource_Keyboard; // or Nav in general
13436
1
}
13437
13438
static ImVec2 ImGui::NavCalcPreferredRefPos()
13439
1
{
13440
1
    ImGuiContext& g = *GImGui;
13441
1
    ImGuiWindow* window = g.NavWindow;
13442
1
    ImGuiInputSource source = NavCalcPreferredRefPosSource();
13443
13444
1
    const bool activated_shortcut = g.ActiveId != 0 && g.ActiveIdFromShortcut && g.ActiveId == g.LastItemData.ID;
13445
13446
1
    if (source != ImGuiInputSource_Mouse && !activated_shortcut && window == NULL)
13447
0
        source = ImGuiInputSource_Mouse;
13448
13449
    // Testing for !activated_shortcut here could in theory be removed if we decided that activating a remote shortcut altered one of the g.NavDisableXXX flag.
13450
1
    if (source == ImGuiInputSource_Mouse)
13451
1
    {
13452
        // Mouse (we need a fallback in case the mouse becomes invalid after being used)
13453
        // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.
13454
        // In theory we could move that +1.0f offset in OpenPopupEx()
13455
1
        ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;
13456
1
        return ImVec2(p.x + 1.0f, p.y);
13457
1
    }
13458
0
    else
13459
0
    {
13460
        // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item
13461
0
        ImRect ref_rect;
13462
0
        if (activated_shortcut)
13463
0
            ref_rect = g.LastItemData.NavRect;
13464
0
        else if (window != NULL)
13465
0
            ref_rect = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);
13466
13467
        // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)
13468
0
        if (window != NULL && window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))
13469
0
        {
13470
0
            ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);
13471
0
            ref_rect.Translate(window->Scroll - next_scroll);
13472
0
        }
13473
0
        ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight()));
13474
0
        ImGuiViewport* viewport = GetMainViewport();
13475
0
        return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.
13476
0
    }
13477
1
}
13478
13479
float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)
13480
0
{
13481
0
    ImGuiContext& g = *GImGui;
13482
0
    float repeat_delay, repeat_rate;
13483
0
    GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);
13484
13485
0
    ImGuiKey key_less, key_more;
13486
0
    if (g.NavInputSource == ImGuiInputSource_Gamepad)
13487
0
    {
13488
0
        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;
13489
0
        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;
13490
0
    }
13491
0
    else
13492
0
    {
13493
0
        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;
13494
0
        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;
13495
0
    }
13496
0
    float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);
13497
0
    if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase
13498
0
        amount = 0.0f;
13499
0
    return amount;
13500
0
}
13501
13502
static void ImGui::NavUpdate()
13503
80.5k
{
13504
80.5k
    ImGuiContext& g = *GImGui;
13505
80.5k
    ImGuiIO& io = g.IO;
13506
13507
80.5k
    io.WantSetMousePos = false;
13508
    //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);
13509
13510
    // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)
13511
    // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?
13512
80.5k
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13513
80.5k
    const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };
13514
80.5k
    if (nav_gamepad_active)
13515
0
        for (ImGuiKey key : nav_gamepad_keys_to_change_source)
13516
0
            if (IsKeyDown(key))
13517
0
                g.NavInputSource = ImGuiInputSource_Gamepad;
13518
80.5k
    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13519
80.5k
    const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };
13520
80.5k
    if (nav_keyboard_active)
13521
80.5k
        for (ImGuiKey key : nav_keyboard_keys_to_change_source)
13522
564k
            if (IsKeyDown(key))
13523
70.5k
                g.NavInputSource = ImGuiInputSource_Keyboard;
13524
13525
    // Process navigation init request (select first/default focus)
13526
80.5k
    g.NavJustMovedToId = 0;
13527
80.5k
    g.NavJustMovedToFocusScopeId = g.NavJustMovedFromFocusScopeId = 0;
13528
80.5k
    if (g.NavInitResult.ID != 0)
13529
1
        NavInitRequestApplyResult();
13530
80.5k
    g.NavInitRequest = false;
13531
80.5k
    g.NavInitRequestFromMove = false;
13532
80.5k
    g.NavInitResult.ID = 0;
13533
13534
    // Process navigation move request
13535
80.5k
    if (g.NavMoveSubmitted)
13536
0
        NavMoveRequestApplyResult();
13537
80.5k
    g.NavTabbingCounter = 0;
13538
80.5k
    g.NavMoveSubmitted = g.NavMoveScoringItems = false;
13539
80.5k
    if (g.NavCursorHideFrames > 0)
13540
0
        if (--g.NavCursorHideFrames == 0)
13541
0
            g.NavCursorVisible = true;
13542
13543
    // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)
13544
80.5k
    bool set_mouse_pos = false;
13545
80.5k
    if (g.NavMousePosDirty && g.NavIdIsAlive)
13546
0
        if (g.NavCursorVisible && g.NavHighlightItemUnderNav && g.NavWindow)
13547
0
            set_mouse_pos = true;
13548
80.5k
    g.NavMousePosDirty = false;
13549
80.5k
    IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);
13550
13551
    // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0
13552
80.5k
    if (g.NavWindow)
13553
662
        NavSaveLastChildNavWindowIntoParent(g.NavWindow);
13554
80.5k
    if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)
13555
0
        g.NavWindow->NavLastChildNavWindow = NULL;
13556
13557
    // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)
13558
80.5k
    NavUpdateWindowing();
13559
13560
    // Set output flags for user application
13561
80.5k
    io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);
13562
80.5k
    io.NavVisible = (io.NavActive && g.NavId != 0 && g.NavCursorVisible) || (g.NavWindowingTarget != NULL);
13563
13564
    // Process NavCancel input (to close a popup, get back to parent, clear focus)
13565
80.5k
    NavUpdateCancelRequest();
13566
13567
    // Process manual activation request
13568
80.5k
    g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;
13569
80.5k
    g.NavActivateFlags = ImGuiActivateFlags_None;
13570
80.5k
    if (g.NavId != 0 && g.NavCursorVisible && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13571
0
    {
13572
0
        const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_NoOwner));
13573
0
        const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, 0, ImGuiKeyOwner_NoOwner)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, 0, ImGuiKeyOwner_NoOwner)));
13574
0
        const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_NoOwner) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_NoOwner));
13575
0
        const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, 0, ImGuiKeyOwner_NoOwner) || IsKeyPressed(ImGuiKey_KeypadEnter, 0, ImGuiKeyOwner_NoOwner))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, 0, ImGuiKeyOwner_NoOwner)));
13576
0
        if (g.ActiveId == 0 && activate_pressed)
13577
0
        {
13578
0
            g.NavActivateId = g.NavId;
13579
0
            g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;
13580
0
        }
13581
0
        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)
13582
0
        {
13583
0
            g.NavActivateId = g.NavId;
13584
0
            g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
13585
0
        }
13586
0
        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))
13587
0
            g.NavActivateDownId = g.NavId;
13588
0
        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))
13589
0
        {
13590
0
            g.NavActivatePressedId = g.NavId;
13591
0
            NavHighlightActivated(g.NavId);
13592
0
        }
13593
0
    }
13594
80.5k
    if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
13595
0
        g.NavCursorVisible = false;
13596
80.5k
    else if (g.IO.ConfigNavCursorVisibleAlways && g.NavCursorHideFrames == 0)
13597
0
        g.NavCursorVisible = true;
13598
80.5k
    if (g.NavActivateId != 0)
13599
80.5k
        IM_ASSERT(g.NavActivateDownId == g.NavActivateId);
13600
13601
    // Highlight
13602
80.5k
    if (g.NavHighlightActivatedTimer > 0.0f)
13603
0
        g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime);
13604
80.5k
    if (g.NavHighlightActivatedTimer == 0.0f)
13605
80.5k
        g.NavHighlightActivatedId = 0;
13606
13607
    // Process programmatic activation request
13608
    // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)
13609
80.5k
    if (g.NavNextActivateId != 0)
13610
0
    {
13611
0
        g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;
13612
0
        g.NavActivateFlags = g.NavNextActivateFlags;
13613
0
    }
13614
80.5k
    g.NavNextActivateId = 0;
13615
13616
    // Process move requests
13617
80.5k
    NavUpdateCreateMoveRequest();
13618
80.5k
    if (g.NavMoveDir == ImGuiDir_None)
13619
80.5k
        NavUpdateCreateTabbingRequest();
13620
80.5k
    NavUpdateAnyRequestFlag();
13621
80.5k
    g.NavIdIsAlive = false;
13622
13623
    // Scrolling
13624
80.5k
    if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)
13625
662
    {
13626
        // *Fallback* manual-scroll with Nav directional keys when window has no navigable item
13627
662
        ImGuiWindow* window = g.NavWindow;
13628
662
        const float scroll_speed = IM_ROUND(window->FontRefSize * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.
13629
662
        const ImGuiDir move_dir = g.NavMoveDir;
13630
662
        if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)
13631
0
        {
13632
0
            if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)
13633
0
                SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
13634
0
            if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)
13635
0
                SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
13636
0
        }
13637
13638
        // *Normal* Manual scroll with LStick
13639
        // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.
13640
662
        if (nav_gamepad_active)
13641
0
        {
13642
0
            const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
13643
0
            const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;
13644
0
            if (scroll_dir.x != 0.0f && window->ScrollbarX)
13645
0
                SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));
13646
0
            if (scroll_dir.y != 0.0f)
13647
0
                SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));
13648
0
        }
13649
662
    }
13650
13651
    // Always prioritize mouse highlight if navigation is disabled
13652
80.5k
    if (!nav_keyboard_active && !nav_gamepad_active)
13653
0
    {
13654
0
        g.NavCursorVisible = false;
13655
0
        g.NavHighlightItemUnderNav = set_mouse_pos = false;
13656
0
    }
13657
13658
    // Update mouse position if requested
13659
    // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)
13660
80.5k
    if (set_mouse_pos && io.ConfigNavMoveSetMousePos && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))
13661
0
        TeleportMousePos(NavCalcPreferredRefPos());
13662
13663
    // [DEBUG]
13664
80.5k
    g.NavScoringDebugCount = 0;
13665
#if IMGUI_DEBUG_NAV_RECTS
13666
    if (ImGuiWindow* debug_window = g.NavWindow)
13667
    {
13668
        ImDrawList* draw_list = GetForegroundDrawList(debug_window);
13669
        int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }
13670
        //if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }
13671
    }
13672
#endif
13673
80.5k
}
13674
13675
void ImGui::NavInitRequestApplyResult()
13676
1
{
13677
    // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)
13678
1
    ImGuiContext& g = *GImGui;
13679
1
    if (!g.NavWindow)
13680
0
        return;
13681
13682
1
    ImGuiNavItemData* result = &g.NavInitResult;
13683
1
    if (g.NavId != result->ID)
13684
1
    {
13685
1
        g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13686
1
        g.NavJustMovedToId = result->ID;
13687
1
        g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13688
1
        g.NavJustMovedToKeyMods = 0;
13689
1
        g.NavJustMovedToIsTabbing = false;
13690
1
        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13691
1
    }
13692
13693
    // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)
13694
    // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.
13695
1
    IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13696
1
    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13697
1
    g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result
13698
1
    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13699
0
        g.NavLastValidSelectionUserData = result->SelectionUserData;
13700
1
    if (g.NavInitRequestFromMove)
13701
0
        SetNavCursorVisibleAfterMove();
13702
1
}
13703
13704
// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position
13705
static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)
13706
0
{
13707
    // Bias initial rect
13708
0
    ImGuiContext& g = *GImGui;
13709
0
    const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;
13710
13711
    // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.
13712
    // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.
13713
    // - But each successful move sets new bias on one axis, only cleared when using mouse.
13714
0
    if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)
13715
0
    {
13716
0
        if (preferred_pos_rel.x == FLT_MAX)
13717
0
            preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;
13718
0
        if (preferred_pos_rel.y == FLT_MAX)
13719
0
            preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;
13720
0
    }
13721
13722
    // Apply general bias on the other axis
13723
0
    if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)
13724
0
        r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;
13725
0
    else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)
13726
0
        r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;
13727
0
}
13728
13729
void ImGui::NavUpdateCreateMoveRequest()
13730
80.5k
{
13731
80.5k
    ImGuiContext& g = *GImGui;
13732
80.5k
    ImGuiIO& io = g.IO;
13733
80.5k
    ImGuiWindow* window = g.NavWindow;
13734
80.5k
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13735
80.5k
    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13736
13737
80.5k
    if (g.NavMoveForwardToNextFrame && window != NULL)
13738
0
    {
13739
        // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)
13740
        // (preserve most state, which were already set by the NavMoveRequestForward() function)
13741
0
        IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);
13742
0
        IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);
13743
0
        IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequestForward %d\n", g.NavMoveDir);
13744
0
    }
13745
80.5k
    else
13746
80.5k
    {
13747
        // Initiate directional inputs request
13748
80.5k
        g.NavMoveDir = ImGuiDir_None;
13749
80.5k
        g.NavMoveFlags = ImGuiNavMoveFlags_None;
13750
80.5k
        g.NavMoveScrollFlags = ImGuiScrollFlags_None;
13751
80.5k
        if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))
13752
662
        {
13753
662
            const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;
13754
662
            if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Left; }
13755
662
            if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Right; }
13756
662
            if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp,    repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow,    repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Up; }
13757
662
            if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown,  repeat_mode, ImGuiKeyOwner_NoOwner)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow,  repeat_mode, ImGuiKeyOwner_NoOwner)))) { g.NavMoveDir = ImGuiDir_Down; }
13758
662
        }
13759
80.5k
        g.NavMoveClipDir = g.NavMoveDir;
13760
80.5k
        g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
13761
80.5k
    }
13762
13763
    // Update PageUp/PageDown/Home/End scroll
13764
    // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?
13765
80.5k
    float scoring_rect_offset_y = 0.0f;
13766
80.5k
    if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
13767
662
        scoring_rect_offset_y = NavUpdatePageUpPageDown();
13768
80.5k
    if (scoring_rect_offset_y != 0.0f)
13769
0
    {
13770
0
        g.NavScoringNoClipRect = window->InnerRect;
13771
0
        g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
13772
0
    }
13773
13774
    // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.
13775
#if IMGUI_DEBUG_NAV_SCORING
13776
    //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))
13777
    //    g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);
13778
    if (io.KeyCtrl)
13779
    {
13780
        if (g.NavMoveDir == ImGuiDir_None)
13781
            g.NavMoveDir = g.NavMoveDirForDebug;
13782
        g.NavMoveClipDir = g.NavMoveDir;
13783
        g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;
13784
    }
13785
#endif
13786
13787
    // Submit
13788
80.5k
    g.NavMoveForwardToNextFrame = false;
13789
80.5k
    if (g.NavMoveDir != ImGuiDir_None)
13790
0
        NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);
13791
13792
    // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)
13793
80.5k
    if (g.NavMoveSubmitted && g.NavId == 0)
13794
0
    {
13795
0
        IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "<NULL>", g.NavLayer);
13796
0
        g.NavInitRequest = g.NavInitRequestFromMove = true;
13797
0
        g.NavInitResult.ID = 0;
13798
0
        if (g.IO.ConfigNavCursorVisibleAuto)
13799
0
            g.NavCursorVisible = true;
13800
0
    }
13801
13802
    // When using gamepad, we project the reference nav bounding box into window visible area.
13803
    // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,
13804
    // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).
13805
80.5k
    if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))
13806
0
    {
13807
0
        bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;
13808
0
        bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;
13809
0
        ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));
13810
13811
        // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)
13812
        // Otherwise 'inner_rect_rel' would be off on the move result frame.
13813
0
        inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);
13814
13815
0
        if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))
13816
0
        {
13817
0
            IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n");
13818
0
            float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f);
13819
0
            float pad_y = ImMin(inner_rect_rel.GetHeight(), window->FontRefSize * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item
13820
0
            inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;
13821
0
            inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;
13822
0
            inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;
13823
0
            inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;
13824
0
            window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);
13825
0
            g.NavId = 0;
13826
0
        }
13827
0
    }
13828
13829
    // Prepare scoring rectangle.
13830
    // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)
13831
80.5k
    ImRect scoring_rect;
13832
80.5k
    if (window != NULL)
13833
662
    {
13834
662
        ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);
13835
662
        scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);
13836
662
        scoring_rect.TranslateY(scoring_rect_offset_y);
13837
662
        if (g.NavMoveSubmitted)
13838
0
            NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);
13839
662
        IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().
13840
        //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
13841
        //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
13842
662
    }
13843
80.5k
    g.NavScoringRect = scoring_rect;
13844
    //g.NavScoringNoClipRect.Add(scoring_rect);
13845
80.5k
}
13846
13847
void ImGui::NavUpdateCreateTabbingRequest()
13848
80.5k
{
13849
80.5k
    ImGuiContext& g = *GImGui;
13850
80.5k
    ImGuiWindow* window = g.NavWindow;
13851
80.5k
    IM_ASSERT(g.NavMoveDir == ImGuiDir_None);
13852
80.5k
    if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))
13853
79.9k
        return;
13854
13855
662
    const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner) && !g.IO.KeyCtrl && !g.IO.KeyAlt;
13856
662
    if (!tab_pressed)
13857
662
        return;
13858
13859
    // Initiate tabbing request
13860
    // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)
13861
    // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.
13862
0
    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13863
0
    if (nav_keyboard_active)
13864
0
        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavCursorVisible == false && g.ActiveId == 0) ? 0 : +1;
13865
0
    else
13866
0
        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;
13867
0
    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;
13868
0
    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;
13869
0
    ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;
13870
0
    NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.
13871
0
    g.NavTabbingCounter = -1;
13872
0
}
13873
13874
// Apply result from previous frame navigation directional move request. Always called from NavUpdate()
13875
void ImGui::NavMoveRequestApplyResult()
13876
0
{
13877
0
    ImGuiContext& g = *GImGui;
13878
#if IMGUI_DEBUG_NAV_SCORING
13879
    if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times
13880
        return;
13881
#endif
13882
13883
    // Select which result to use
13884
0
    ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;
13885
13886
    // Tabbing forward wrap
13887
0
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)
13888
0
        if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)
13889
0
            result = &g.NavTabbingResultFirst;
13890
13891
    // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)
13892
0
    const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
13893
0
    if (result == NULL)
13894
0
    {
13895
0
        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13896
0
            g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavCursorVisible;
13897
0
        if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13898
0
            SetNavCursorVisibleAfterMove();
13899
0
        NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.
13900
0
        IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n");
13901
0
        return;
13902
0
    }
13903
13904
    // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.
13905
0
    if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)
13906
0
        if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)
13907
0
            result = &g.NavMoveResultLocalVisible;
13908
13909
    // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.
13910
0
    if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)
13911
0
        if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))
13912
0
            result = &g.NavMoveResultOther;
13913
0
    IM_ASSERT(g.NavWindow && result->Window);
13914
13915
    // Scroll to keep newly navigated item fully into view.
13916
0
    if (g.NavLayer == ImGuiNavLayer_Main)
13917
0
    {
13918
0
        ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);
13919
0
        ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);
13920
13921
0
        if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)
13922
0
        {
13923
            // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?
13924
0
            float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;
13925
0
            SetScrollY(result->Window, scroll_target);
13926
0
        }
13927
0
    }
13928
13929
0
    if (g.NavWindow != result->Window)
13930
0
    {
13931
0
        IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name);
13932
0
        g.NavWindow = result->Window;
13933
0
        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;
13934
0
    }
13935
13936
    // Clear active id unless requested not to
13937
    // FIXME: ImGuiNavMoveFlags_NoClearActiveId is currently unused as we don't have a clear strategy to preserve active id after interaction,
13938
    // so this is mostly provided as a gateway for further experiments (see #1418, #2890)
13939
0
    if (g.ActiveId != result->ID && (g.NavMoveFlags & ImGuiNavMoveFlags_NoClearActiveId) == 0)
13940
0
        ClearActiveID();
13941
13942
    // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)
13943
    // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.
13944
0
    if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)
13945
0
    {
13946
0
        g.NavJustMovedFromFocusScopeId = g.NavFocusScopeId;
13947
0
        g.NavJustMovedToId = result->ID;
13948
0
        g.NavJustMovedToFocusScopeId = result->FocusScopeId;
13949
0
        g.NavJustMovedToKeyMods = g.NavMoveKeyMods;
13950
0
        g.NavJustMovedToIsTabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;
13951
0
        g.NavJustMovedToHasSelectionData = (result->ItemFlags & ImGuiItemFlags_HasSelectionUserData) != 0;
13952
        //IMGUI_DEBUG_LOG_NAV("[nav] NavJustMovedFromFocusScopeId = 0x%08X, NavJustMovedToFocusScopeId = 0x%08X\n", g.NavJustMovedFromFocusScopeId, g.NavJustMovedToFocusScopeId);
13953
0
    }
13954
13955
    // Apply new NavID/Focus
13956
0
    IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name);
13957
0
    ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];
13958
0
    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);
13959
0
    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)
13960
0
        g.NavLastValidSelectionUserData = result->SelectionUserData;
13961
13962
    // Restore last preferred position for current axis
13963
    // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)
13964
0
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)
13965
0
    {
13966
0
        preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];
13967
0
        g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;
13968
0
    }
13969
13970
    // Tabbing: Activates Inputable, otherwise only Focus
13971
0
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->ItemFlags & ImGuiItemFlags_Inputable) == 0)
13972
0
        g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;
13973
13974
    // Activate
13975
0
    if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)
13976
0
    {
13977
0
        g.NavNextActivateId = result->ID;
13978
0
        g.NavNextActivateFlags = ImGuiActivateFlags_None;
13979
0
        if (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)
13980
0
            g.NavNextActivateFlags |= ImGuiActivateFlags_FromFocusApi;
13981
0
        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)
13982
0
            g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing;
13983
0
    }
13984
13985
    // Make nav cursor visible
13986
0
    if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavCursorVisible) == 0)
13987
0
        SetNavCursorVisibleAfterMove();
13988
0
}
13989
13990
// Process Escape/NavCancel input (to close a popup, get back to parent, clear focus)
13991
// FIXME: In order to support e.g. Escape to clear a selection we'll need:
13992
// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.
13993
// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept
13994
static void ImGui::NavUpdateCancelRequest()
13995
80.5k
{
13996
80.5k
    ImGuiContext& g = *GImGui;
13997
80.5k
    const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
13998
80.5k
    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
13999
80.5k
    if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, 0, ImGuiKeyOwner_NoOwner)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, 0, ImGuiKeyOwner_NoOwner)))
14000
80.5k
        return;
14001
14002
0
    IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n");
14003
0
    if (g.ActiveId != 0)
14004
0
    {
14005
0
        ClearActiveID();
14006
0
    }
14007
0
    else if (g.NavLayer != ImGuiNavLayer_Main)
14008
0
    {
14009
        // Leave the "menu" layer
14010
0
        NavRestoreLayer(ImGuiNavLayer_Main);
14011
0
        SetNavCursorVisibleAfterMove();
14012
0
    }
14013
0
    else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow)
14014
0
    {
14015
        // Exit child window
14016
0
        ImGuiWindow* child_window = g.NavWindow->RootWindowForNav;
14017
0
        ImGuiWindow* parent_window = child_window->ParentWindow;
14018
0
        IM_ASSERT(child_window->ChildId != 0);
14019
0
        FocusWindow(parent_window);
14020
0
        SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect()));
14021
0
        SetNavCursorVisibleAfterMove();
14022
0
    }
14023
0
    else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))
14024
0
    {
14025
        // Close open popup/menu
14026
0
        ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);
14027
0
    }
14028
0
    else
14029
0
    {
14030
        // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were
14031
        // FIXME-NAV: This should happen on window appearing.
14032
0
        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14033
0
            if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup)))// || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))
14034
0
                g.NavWindow->NavLastIds[0] = 0;
14035
14036
        // Clear nav focus
14037
0
        if (g.IO.ConfigNavEscapeClearFocusItem || g.IO.ConfigNavEscapeClearFocusWindow)
14038
0
            g.NavId = 0;
14039
0
        if (g.IO.ConfigNavEscapeClearFocusWindow)
14040
0
            FocusWindow(NULL);
14041
0
    }
14042
0
}
14043
14044
// Handle PageUp/PageDown/Home/End keys
14045
// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request
14046
// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference
14047
// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?
14048
static float ImGui::NavUpdatePageUpPageDown()
14049
662
{
14050
662
    ImGuiContext& g = *GImGui;
14051
662
    ImGuiWindow* window = g.NavWindow;
14052
662
    if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)
14053
0
        return 0.0f;
14054
14055
662
    const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_NoOwner);
14056
662
    const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_NoOwner);
14057
662
    const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14058
662
    const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner);
14059
662
    if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out
14060
662
        return 0.0f;
14061
14062
0
    if (g.NavLayer != ImGuiNavLayer_Main)
14063
0
        NavRestoreLayer(ImGuiNavLayer_Main);
14064
14065
0
    if ((window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Main)) == 0 && window->DC.NavWindowHasScrollY)
14066
0
    {
14067
        // Fallback manual-scroll when window has no navigable item
14068
0
        if (IsKeyPressed(ImGuiKey_PageUp, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14069
0
            SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
14070
0
        else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiInputFlags_Repeat, ImGuiKeyOwner_NoOwner))
14071
0
            SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
14072
0
        else if (home_pressed)
14073
0
            SetScrollY(window, 0.0f);
14074
0
        else if (end_pressed)
14075
0
            SetScrollY(window, window->ScrollMax.y);
14076
0
    }
14077
0
    else
14078
0
    {
14079
0
        ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];
14080
0
        const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight());
14081
0
        float nav_scoring_rect_offset_y = 0.0f;
14082
0
        if (IsKeyPressed(ImGuiKey_PageUp, true))
14083
0
        {
14084
0
            nav_scoring_rect_offset_y = -page_offset_y;
14085
0
            g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)
14086
0
            g.NavMoveClipDir = ImGuiDir_Up;
14087
0
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14088
0
        }
14089
0
        else if (IsKeyPressed(ImGuiKey_PageDown, true))
14090
0
        {
14091
0
            nav_scoring_rect_offset_y = +page_offset_y;
14092
0
            g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)
14093
0
            g.NavMoveClipDir = ImGuiDir_Down;
14094
0
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;
14095
0
        }
14096
0
        else if (home_pressed)
14097
0
        {
14098
            // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y
14099
            // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.
14100
            // Preserve current horizontal position if we have any.
14101
0
            nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;
14102
0
            if (nav_rect_rel.IsInverted())
14103
0
                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14104
0
            g.NavMoveDir = ImGuiDir_Down;
14105
0
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14106
            // FIXME-NAV: MoveClipDir left to _None, intentional?
14107
0
        }
14108
0
        else if (end_pressed)
14109
0
        {
14110
0
            nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;
14111
0
            if (nav_rect_rel.IsInverted())
14112
0
                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;
14113
0
            g.NavMoveDir = ImGuiDir_Up;
14114
0
            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;
14115
            // FIXME-NAV: MoveClipDir left to _None, intentional?
14116
0
        }
14117
0
        return nav_scoring_rect_offset_y;
14118
0
    }
14119
0
    return 0.0f;
14120
0
}
14121
14122
static void ImGui::NavEndFrame()
14123
80.5k
{
14124
80.5k
    ImGuiContext& g = *GImGui;
14125
14126
    // Show CTRL+TAB list window
14127
80.5k
    if (g.NavWindowingTarget != NULL)
14128
0
        NavUpdateWindowingOverlay();
14129
14130
    // Perform wrap-around in menus
14131
    // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.
14132
    // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.
14133
80.5k
    if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
14134
0
        NavUpdateCreateWrappingRequest();
14135
80.5k
}
14136
14137
static void ImGui::NavUpdateCreateWrappingRequest()
14138
0
{
14139
0
    ImGuiContext& g = *GImGui;
14140
0
    ImGuiWindow* window = g.NavWindow;
14141
14142
0
    bool do_forward = false;
14143
0
    ImRect bb_rel = window->NavRectRel[g.NavLayer];
14144
0
    ImGuiDir clip_dir = g.NavMoveDir;
14145
14146
0
    const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;
14147
    //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
14148
0
    if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14149
0
    {
14150
0
        bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;
14151
0
        if (move_flags & ImGuiNavMoveFlags_WrapX)
14152
0
        {
14153
0
            bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row
14154
0
            clip_dir = ImGuiDir_Up;
14155
0
        }
14156
0
        do_forward = true;
14157
0
    }
14158
0
    if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))
14159
0
    {
14160
0
        bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;
14161
0
        if (move_flags & ImGuiNavMoveFlags_WrapX)
14162
0
        {
14163
0
            bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row
14164
0
            clip_dir = ImGuiDir_Down;
14165
0
        }
14166
0
        do_forward = true;
14167
0
    }
14168
0
    if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14169
0
    {
14170
0
        bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;
14171
0
        if (move_flags & ImGuiNavMoveFlags_WrapY)
14172
0
        {
14173
0
            bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column
14174
0
            clip_dir = ImGuiDir_Left;
14175
0
        }
14176
0
        do_forward = true;
14177
0
    }
14178
0
    if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))
14179
0
    {
14180
0
        bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;
14181
0
        if (move_flags & ImGuiNavMoveFlags_WrapY)
14182
0
        {
14183
0
            bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column
14184
0
            clip_dir = ImGuiDir_Right;
14185
0
        }
14186
0
        do_forward = true;
14187
0
    }
14188
0
    if (!do_forward)
14189
0
        return;
14190
0
    window->NavRectRel[g.NavLayer] = bb_rel;
14191
0
    NavClearPreferredPosForAxis(ImGuiAxis_X);
14192
0
    NavClearPreferredPosForAxis(ImGuiAxis_Y);
14193
0
    NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);
14194
0
}
14195
14196
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
14197
// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.
14198
// If you want a window to never be focused, you may use the e.g. NoInputs flag.
14199
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
14200
0
{
14201
0
    return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
14202
0
}
14203
14204
static ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)
14205
0
{
14206
0
    ImGuiContext& g = *GImGui;
14207
0
    for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)
14208
0
        if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))
14209
0
            return g.WindowsFocusOrder[i];
14210
0
    return NULL;
14211
0
}
14212
14213
static void NavUpdateWindowingTarget(int focus_change_dir)
14214
0
{
14215
0
    ImGuiContext& g = *GImGui;
14216
0
    IM_ASSERT(g.NavWindowingTarget);
14217
0
    if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)
14218
0
        return;
14219
14220
0
    const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);
14221
0
    ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);
14222
0
    if (!window_target)
14223
0
        window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);
14224
0
    if (window_target) // Don't reset windowing target if there's a single window in the list
14225
0
    {
14226
0
        g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;
14227
0
        g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14228
0
    }
14229
0
    g.NavWindowingToggleLayer = false;
14230
0
}
14231
14232
// Apply focus and close overlay
14233
static void ImGui::NavUpdateWindowingApplyFocus(ImGuiWindow* apply_focus_window)
14234
0
{
14235
0
    ImGuiContext& g = *GImGui;
14236
0
    if (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)
14237
0
    {
14238
0
        ClearActiveID();
14239
0
        SetNavCursorVisibleAfterMove();
14240
0
        ClosePopupsOverWindow(apply_focus_window, false);
14241
0
        FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);
14242
0
        IM_ASSERT(g.NavWindow != NULL);
14243
0
        apply_focus_window = g.NavWindow;
14244
0
        if (apply_focus_window->NavLastIds[0] == 0)
14245
0
            NavInitWindow(apply_focus_window, false);
14246
14247
        // If the window has ONLY a menu layer (no main layer), select it directly
14248
        // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,
14249
        // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since
14250
        // the target window as already been previewed once.
14251
        // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,
14252
        // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*
14253
        // won't be valid.
14254
0
        if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))
14255
0
            g.NavLayer = ImGuiNavLayer_Menu;
14256
0
    }
14257
0
    g.NavWindowingTarget = NULL;
14258
0
}
14259
14260
// Windowing management mode
14261
// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)
14262
// Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)
14263
static void ImGui::NavUpdateWindowing()
14264
80.5k
{
14265
80.5k
    ImGuiContext& g = *GImGui;
14266
80.5k
    ImGuiIO& io = g.IO;
14267
14268
80.5k
    ImGuiWindow* apply_focus_window = NULL;
14269
80.5k
    bool apply_toggle_layer = false;
14270
14271
80.5k
    ImGuiWindow* modal_window = GetTopMostPopupModal();
14272
80.5k
    bool allow_windowing = (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal.
14273
80.5k
    if (!allow_windowing)
14274
0
        g.NavWindowingTarget = NULL;
14275
14276
    // Fade out
14277
80.5k
    if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)
14278
0
    {
14279
0
        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);
14280
0
        if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)
14281
0
            g.NavWindowingTargetAnim = NULL;
14282
0
    }
14283
14284
    // Start CTRL+Tab or Square+L/R window selection
14285
    // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab)
14286
80.5k
    const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing");
14287
80.5k
    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
14288
80.5k
    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;
14289
80.5k
    const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14290
80.5k
    const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id);
14291
80.5k
    const bool start_toggling_with_gamepad = nav_gamepad_active && !g.NavWindowingTarget && Shortcut(ImGuiKey_NavGamepadMenu, ImGuiInputFlags_RouteAlways, owner_id);
14292
80.5k
    const bool start_windowing_with_gamepad = allow_windowing && start_toggling_with_gamepad;
14293
80.5k
    const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!
14294
80.5k
    bool just_started_windowing_from_null_focus = false;
14295
80.5k
    if (start_toggling_with_gamepad)
14296
0
    {
14297
0
        g.NavWindowingToggleLayer = true; // Gamepad starts toggling layer
14298
0
        g.NavWindowingToggleKey = ImGuiKey_NavGamepadMenu;
14299
0
        g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad;
14300
0
    }
14301
80.5k
    if (start_windowing_with_gamepad || start_windowing_with_keyboard)
14302
0
        if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))
14303
0
        {
14304
0
            if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad)
14305
0
                g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location
14306
0
            g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;
14307
0
            g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);
14308
0
            g.NavWindowingInputSource = g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;
14309
0
            if (g.NavWindow == NULL)
14310
0
                just_started_windowing_from_null_focus = true;
14311
14312
            // Manually register ownership of our mods. Using a global route in the Shortcut() calls instead would probably be correct but may have more side-effects.
14313
0
            if (keyboard_next_window || keyboard_prev_window)
14314
0
                SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);
14315
0
        }
14316
14317
    // Gamepad update
14318
80.5k
    if ((g.NavWindowingTarget || g.NavWindowingToggleLayer) && g.NavWindowingInputSource == ImGuiInputSource_Gamepad)
14319
0
    {
14320
0
        if (g.NavWindowingTarget != NULL)
14321
0
        {
14322
            // Highlight only appears after a brief time holding the button, so that a fast tap on ImGuiKey_NavGamepadMenu (to toggle NavLayer) doesn't add visual noise
14323
            // However inputs are accepted immediately, so you press ImGuiKey_NavGamepadMenu + L1/R1 fast.
14324
0
            g.NavWindowingTimer += io.DeltaTime;
14325
0
            g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));
14326
14327
            // Select window to focus
14328
0
            const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);
14329
0
            if (focus_change_dir != 0 && !just_started_windowing_from_null_focus)
14330
0
            {
14331
0
                NavUpdateWindowingTarget(focus_change_dir);
14332
0
                g.NavWindowingHighlightAlpha = 1.0f;
14333
0
            }
14334
0
        }
14335
14336
        // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)
14337
0
        if (!IsKeyDown(ImGuiKey_NavGamepadMenu))
14338
0
        {
14339
0
            g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.
14340
0
            if (g.NavWindowingToggleLayer && g.NavWindow)
14341
0
                apply_toggle_layer = true;
14342
0
            else if (!g.NavWindowingToggleLayer)
14343
0
                apply_focus_window = g.NavWindowingTarget;
14344
0
            g.NavWindowingTarget = NULL;
14345
0
            g.NavWindowingToggleLayer = false;
14346
0
        }
14347
0
    }
14348
14349
    // Keyboard: Focus
14350
80.5k
    if (g.NavWindowingTarget && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)
14351
0
    {
14352
        // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise
14353
0
        ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;
14354
0
        IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows.
14355
0
        g.NavWindowingTimer += io.DeltaTime;
14356
0
        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f
14357
0
        if ((keyboard_next_window || keyboard_prev_window) && !just_started_windowing_from_null_focus)
14358
0
            NavUpdateWindowingTarget(keyboard_next_window ? -1 : +1);
14359
0
        else if ((io.KeyMods & shared_mods) != shared_mods)
14360
0
            apply_focus_window = g.NavWindowingTarget;
14361
0
    }
14362
14363
    // Keyboard: Press and Release ALT to toggle menu layer
14364
80.5k
    const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt };
14365
80.5k
    bool windowing_toggle_layer_start = false;
14366
80.5k
    if (g.NavWindow != NULL && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))
14367
662
        for (ImGuiKey windowing_toggle_key : windowing_toggle_keys)
14368
1.32k
            if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, 0, ImGuiKeyOwner_NoOwner))
14369
0
            {
14370
0
                windowing_toggle_layer_start = true;
14371
0
                g.NavWindowingToggleLayer = true;
14372
0
                g.NavWindowingToggleKey = windowing_toggle_key;
14373
0
                g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Keyboard;
14374
0
                break;
14375
0
            }
14376
80.5k
    if (g.NavWindowingToggleLayer && g.NavWindowingInputSource == ImGuiInputSource_Keyboard)
14377
0
    {
14378
        // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)
14379
        // We cancel toggling nav layer when other modifiers are pressed. (See #4439)
14380
        // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl).
14381
        // We cancel toggling nav layer if an owner has claimed the key.
14382
0
        if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper)
14383
0
            g.NavWindowingToggleLayer = false;
14384
0
        else if (windowing_toggle_layer_start == false && g.LastKeyboardKeyPressTime == g.Time)
14385
0
            g.NavWindowingToggleLayer = false;
14386
0
        else if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_NoOwner) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_NoOwner) == false)
14387
0
            g.NavWindowingToggleLayer = false;
14388
14389
        // Apply layer toggle on Alt release
14390
        // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.
14391
0
        if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer)
14392
0
            if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)
14393
0
                if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))
14394
0
                    apply_toggle_layer = true;
14395
0
        if (!IsKeyDown(g.NavWindowingToggleKey))
14396
0
            g.NavWindowingToggleLayer = false;
14397
0
    }
14398
14399
    // Move window
14400
80.5k
    if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))
14401
0
    {
14402
0
        ImVec2 nav_move_dir;
14403
0
        if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)
14404
0
            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);
14405
0
        if (g.NavInputSource == ImGuiInputSource_Gamepad)
14406
0
            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);
14407
0
        if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)
14408
0
        {
14409
0
            const float NAV_MOVE_SPEED = 800.0f;
14410
0
            const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
14411
0
            g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;
14412
0
            g.NavHighlightItemUnderNav = true;
14413
0
            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);
14414
0
            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)
14415
0
            {
14416
0
                ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;
14417
0
                SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);
14418
0
                g.NavWindowingAccumDeltaPos -= accum_floored;
14419
0
            }
14420
0
        }
14421
0
    }
14422
14423
    // Apply final focus
14424
80.5k
    if (apply_focus_window)
14425
0
        NavUpdateWindowingApplyFocus(apply_focus_window);
14426
14427
    // Apply menu/layer toggle
14428
80.5k
    if (apply_toggle_layer && g.NavWindow)
14429
0
    {
14430
0
        ClearActiveID();
14431
14432
        // Move to parent menu if necessary
14433
0
        ImGuiWindow* new_nav_window = g.NavWindow;
14434
0
        while (new_nav_window->ParentWindow
14435
0
            && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0
14436
0
            && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
14437
0
            && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
14438
0
            new_nav_window = new_nav_window->ParentWindow;
14439
0
        if (new_nav_window != g.NavWindow)
14440
0
        {
14441
0
            ImGuiWindow* old_nav_window = g.NavWindow;
14442
0
            FocusWindow(new_nav_window);
14443
0
            new_nav_window->NavLastChildNavWindow = old_nav_window;
14444
0
        }
14445
14446
        // Toggle layer
14447
0
        const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;
14448
0
        if (new_nav_layer != g.NavLayer)
14449
0
        {
14450
            // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)
14451
0
            if (new_nav_layer == ImGuiNavLayer_Menu)
14452
0
                g.NavWindow->NavLastIds[new_nav_layer] = 0;
14453
0
            NavRestoreLayer(new_nav_layer);
14454
0
            SetNavCursorVisibleAfterMove();
14455
0
        }
14456
0
    }
14457
80.5k
}
14458
14459
// Window has already passed the IsWindowNavFocusable()
14460
static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
14461
0
{
14462
0
    if (window->Flags & ImGuiWindowFlags_Popup)
14463
0
        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);
14464
0
    if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
14465
0
        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);
14466
0
    return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);
14467
0
}
14468
14469
// Overlay displayed when using CTRL+TAB. Called by EndFrame().
14470
void ImGui::NavUpdateWindowingOverlay()
14471
0
{
14472
0
    ImGuiContext& g = *GImGui;
14473
0
    IM_ASSERT(g.NavWindowingTarget != NULL);
14474
14475
0
    if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)
14476
0
        return;
14477
14478
0
    if (g.NavWindowingListWindow == NULL)
14479
0
        g.NavWindowingListWindow = FindWindowByName("##NavWindowingOverlay");
14480
0
    const ImGuiViewport* viewport = GetMainViewport();
14481
0
    SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));
14482
0
    SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
14483
0
    PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);
14484
0
    Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);
14485
0
    if (g.ContextName[0] != 0)
14486
0
        SeparatorText(g.ContextName);
14487
0
    for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)
14488
0
    {
14489
0
        ImGuiWindow* window = g.WindowsFocusOrder[n];
14490
0
        IM_ASSERT(window != NULL); // Fix static analyzers
14491
0
        if (!IsWindowNavFocusable(window))
14492
0
            continue;
14493
0
        const char* label = window->Name;
14494
0
        if (label == FindRenderedTextEnd(label))
14495
0
            label = GetFallbackWindowNameForWindowingList(window);
14496
0
        Selectable(label, g.NavWindowingTarget == window);
14497
0
    }
14498
0
    End();
14499
0
    PopStyleVar();
14500
0
}
14501
14502
//-----------------------------------------------------------------------------
14503
// [SECTION] DRAG AND DROP
14504
//-----------------------------------------------------------------------------
14505
14506
bool ImGui::IsDragDropActive()
14507
0
{
14508
0
    ImGuiContext& g = *GImGui;
14509
0
    return g.DragDropActive;
14510
0
}
14511
14512
void ImGui::ClearDragDrop()
14513
0
{
14514
0
    ImGuiContext& g = *GImGui;
14515
0
    if (g.DragDropActive)
14516
0
        IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] ClearDragDrop()\n");
14517
0
    g.DragDropActive = false;
14518
0
    g.DragDropPayload.Clear();
14519
0
    g.DragDropAcceptFlags = ImGuiDragDropFlags_None;
14520
0
    g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;
14521
0
    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;
14522
0
    g.DragDropAcceptFrameCount = -1;
14523
14524
0
    g.DragDropPayloadBufHeap.clear();
14525
0
    memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14526
0
}
14527
14528
bool ImGui::BeginTooltipHidden()
14529
0
{
14530
0
    ImGuiContext& g = *GImGui;
14531
0
    bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);
14532
0
    SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow);
14533
0
    return ret;
14534
0
}
14535
14536
// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()
14537
// If the item has an identifier:
14538
// - This assume/require the item to be activated (typically via ButtonBehavior).
14539
// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.
14540
// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.
14541
// If the item has no identifier:
14542
// - Currently always assume left mouse button.
14543
bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
14544
0
{
14545
0
    ImGuiContext& g = *GImGui;
14546
0
    ImGuiWindow* window = g.CurrentWindow;
14547
14548
    // FIXME-DRAGDROP: While in the common-most "drag from non-zero active id" case we can tell the mouse button,
14549
    // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).
14550
0
    ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;
14551
14552
0
    bool source_drag_active = false;
14553
0
    ImGuiID source_id = 0;
14554
0
    ImGuiID source_parent_id = 0;
14555
0
    if ((flags & ImGuiDragDropFlags_SourceExtern) == 0)
14556
0
    {
14557
0
        source_id = g.LastItemData.ID;
14558
0
        if (source_id != 0)
14559
0
        {
14560
            // Common path: items with ID
14561
0
            if (g.ActiveId != source_id)
14562
0
                return false;
14563
0
            if (g.ActiveIdMouseButton != -1)
14564
0
                mouse_button = g.ActiveIdMouseButton;
14565
0
            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14566
0
                return false;
14567
0
            g.ActiveIdAllowOverlap = false;
14568
0
        }
14569
0
        else
14570
0
        {
14571
            // Uncommon path: items without ID
14572
0
            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)
14573
0
                return false;
14574
0
            if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))
14575
0
                return false;
14576
14577
            // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:
14578
            // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.
14579
0
            if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))
14580
0
            {
14581
0
                IM_ASSERT(0);
14582
0
                return false;
14583
0
            }
14584
14585
            // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()
14586
            // We build a throwaway ID based on current ID stack + relative AABB of items in window.
14587
            // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.
14588
            // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.
14589
            // Rely on keeping other window->LastItemXXX fields intact.
14590
0
            source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);
14591
0
            KeepAliveID(source_id);
14592
0
            bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.ItemFlags);
14593
0
            if (is_hovered && g.IO.MouseClicked[mouse_button])
14594
0
            {
14595
0
                SetActiveID(source_id, window);
14596
0
                FocusWindow(window);
14597
0
            }
14598
0
            if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.
14599
0
                g.ActiveIdAllowOverlap = is_hovered;
14600
0
        }
14601
0
        if (g.ActiveId != source_id)
14602
0
            return false;
14603
0
        source_parent_id = window->IDStack.back();
14604
0
        source_drag_active = IsMouseDragging(mouse_button);
14605
14606
        // Disable navigation and key inputs while dragging + cancel existing request if any
14607
0
        SetActiveIdUsingAllKeyboardKeys();
14608
0
    }
14609
0
    else
14610
0
    {
14611
        // When ImGuiDragDropFlags_SourceExtern is set:
14612
0
        window = NULL;
14613
0
        source_id = ImHashStr("#SourceExtern");
14614
0
        source_drag_active = true;
14615
0
        mouse_button = g.IO.MouseDown[0] ? 0 : -1;
14616
0
        KeepAliveID(source_id);
14617
0
        SetActiveID(source_id, NULL);
14618
0
    }
14619
14620
0
    IM_ASSERT(g.DragDropWithinTarget == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14621
0
    if (!source_drag_active)
14622
0
        return false;
14623
14624
    // Activate drag and drop
14625
0
    if (!g.DragDropActive)
14626
0
    {
14627
0
        IM_ASSERT(source_id != 0);
14628
0
        ClearDragDrop();
14629
0
        IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] BeginDragDropSource() DragDropActive = true, source_id = 0x%08X%s\n",
14630
0
            source_id, (flags & ImGuiDragDropFlags_SourceExtern) ? " (EXTERN)" : "");
14631
0
        ImGuiPayload& payload = g.DragDropPayload;
14632
0
        payload.SourceId = source_id;
14633
0
        payload.SourceParentId = source_parent_id;
14634
0
        g.DragDropActive = true;
14635
0
        g.DragDropSourceFlags = flags;
14636
0
        g.DragDropMouseButton = mouse_button;
14637
0
        if (payload.SourceId == g.ActiveId)
14638
0
            g.ActiveIdNoClearOnFocusLoss = true;
14639
0
    }
14640
0
    g.DragDropSourceFrameCount = g.FrameCount;
14641
0
    g.DragDropWithinSource = true;
14642
14643
0
    if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14644
0
    {
14645
        // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)
14646
        // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.
14647
0
        bool ret;
14648
0
        if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))
14649
0
            ret = BeginTooltipHidden();
14650
0
        else
14651
0
            ret = BeginTooltip();
14652
0
        IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().
14653
0
        IM_UNUSED(ret);
14654
0
    }
14655
14656
0
    if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))
14657
0
        g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;
14658
14659
0
    return true;
14660
0
}
14661
14662
void ImGui::EndDragDropSource()
14663
0
{
14664
0
    ImGuiContext& g = *GImGui;
14665
0
    IM_ASSERT(g.DragDropActive);
14666
0
    IM_ASSERT(g.DragDropWithinSource && "Not after a BeginDragDropSource()?");
14667
14668
0
    if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))
14669
0
        EndTooltip();
14670
14671
    // Discard the drag if have not called SetDragDropPayload()
14672
0
    if (g.DragDropPayload.DataFrameCount == -1)
14673
0
        ClearDragDrop();
14674
0
    g.DragDropWithinSource = false;
14675
0
}
14676
14677
// Use 'cond' to choose to submit payload on drag start or every frame
14678
bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)
14679
0
{
14680
0
    ImGuiContext& g = *GImGui;
14681
0
    ImGuiPayload& payload = g.DragDropPayload;
14682
0
    if (cond == 0)
14683
0
        cond = ImGuiCond_Always;
14684
14685
0
    IM_ASSERT(type != NULL);
14686
0
    IM_ASSERT(ImStrlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long");
14687
0
    IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));
14688
0
    IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);
14689
0
    IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource()
14690
14691
0
    if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)
14692
0
    {
14693
        // Copy payload
14694
0
        ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));
14695
0
        g.DragDropPayloadBufHeap.resize(0);
14696
0
        if (data_size > sizeof(g.DragDropPayloadBufLocal))
14697
0
        {
14698
            // Store in heap
14699
0
            g.DragDropPayloadBufHeap.resize((int)data_size);
14700
0
            payload.Data = g.DragDropPayloadBufHeap.Data;
14701
0
            memcpy(payload.Data, data, data_size);
14702
0
        }
14703
0
        else if (data_size > 0)
14704
0
        {
14705
            // Store locally
14706
0
            memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));
14707
0
            payload.Data = g.DragDropPayloadBufLocal;
14708
0
            memcpy(payload.Data, data, data_size);
14709
0
        }
14710
0
        else
14711
0
        {
14712
0
            payload.Data = NULL;
14713
0
        }
14714
0
        payload.DataSize = (int)data_size;
14715
0
    }
14716
0
    payload.DataFrameCount = g.FrameCount;
14717
14718
    // Return whether the payload has been accepted
14719
0
    return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);
14720
0
}
14721
14722
bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
14723
0
{
14724
0
    ImGuiContext& g = *GImGui;
14725
0
    if (!g.DragDropActive)
14726
0
        return false;
14727
14728
0
    ImGuiWindow* window = g.CurrentWindow;
14729
0
    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14730
0
    if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)
14731
0
        return false;
14732
0
    IM_ASSERT(id != 0);
14733
0
    if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
14734
0
        return false;
14735
0
    if (window->SkipItems)
14736
0
        return false;
14737
14738
0
    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14739
0
    g.DragDropTargetRect = bb;
14740
0
    g.DragDropTargetClipRect = window->ClipRect; // May want to be overridden by user depending on use case?
14741
0
    g.DragDropTargetId = id;
14742
0
    g.DragDropTargetFullViewport = 0;
14743
0
    g.DragDropWithinTarget = true;
14744
0
    return true;
14745
0
}
14746
14747
// Typical usage would be:
14748
//   if (!ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
14749
//       if (ImGui::BeginDragDropTargetViewport(ImGui::GetMainViewport(), NULL))
14750
// But we are leaving the hover test to the caller for maximum flexibility.
14751
bool ImGui::BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb)
14752
0
{
14753
0
    ImGuiContext& g = *GImGui;
14754
0
    if (!g.DragDropActive)
14755
0
        return false;
14756
14757
0
    ImRect bb = p_bb ? *p_bb : ((ImGuiViewportP*)viewport)->GetWorkRect();
14758
0
    ImGuiID id = viewport->ID;
14759
0
    if (!IsMouseHoveringRect(bb.Min, bb.Max, false) || (id == g.DragDropPayload.SourceId))
14760
0
        return false;
14761
14762
0
    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14763
0
    g.DragDropTargetRect = bb;
14764
0
    g.DragDropTargetClipRect = bb;
14765
0
    g.DragDropTargetId = id;
14766
0
    g.DragDropTargetFullViewport = id;
14767
0
    g.DragDropWithinTarget = true;
14768
0
    return true;
14769
0
}
14770
14771
// We don't use BeginDragDropTargetCustom() and duplicate its code because:
14772
// 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.
14773
// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.
14774
// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)
14775
bool ImGui::BeginDragDropTarget()
14776
0
{
14777
0
    ImGuiContext& g = *GImGui;
14778
0
    if (!g.DragDropActive)
14779
0
        return false;
14780
14781
0
    ImGuiWindow* window = g.CurrentWindow;
14782
0
    if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))
14783
0
        return false;
14784
0
    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;
14785
0
    if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)
14786
0
        return false;
14787
14788
0
    const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;
14789
0
    ImGuiID id = g.LastItemData.ID;
14790
0
    if (id == 0)
14791
0
    {
14792
0
        id = window->GetIDFromRectangle(display_rect);
14793
0
        KeepAliveID(id);
14794
0
    }
14795
0
    if (g.DragDropPayload.SourceId == id)
14796
0
        return false;
14797
14798
0
    IM_ASSERT(g.DragDropWithinTarget == false && g.DragDropWithinSource == false); // Can't nest BeginDragDropSource() and BeginDragDropTarget()
14799
0
    g.DragDropTargetRect = display_rect;
14800
0
    g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect;
14801
0
    g.DragDropTargetId = id;
14802
0
    g.DragDropWithinTarget = true;
14803
0
    return true;
14804
0
}
14805
14806
bool ImGui::IsDragDropPayloadBeingAccepted()
14807
0
{
14808
0
    ImGuiContext& g = *GImGui;
14809
0
    return g.DragDropActive && g.DragDropAcceptIdPrev != 0;
14810
0
}
14811
14812
const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)
14813
0
{
14814
0
    ImGuiContext& g = *GImGui;
14815
0
    ImGuiPayload& payload = g.DragDropPayload;
14816
0
    IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?
14817
0
    IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?
14818
0
    if (type != NULL && !payload.IsDataType(type))
14819
0
        return NULL;
14820
14821
    // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.
14822
    // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!
14823
0
    const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);
14824
0
    ImRect r = g.DragDropTargetRect;
14825
0
    float r_surface = r.GetWidth() * r.GetHeight();
14826
0
    if (r_surface > g.DragDropAcceptIdCurrRectSurface)
14827
0
        return NULL;
14828
14829
0
    g.DragDropAcceptFlags = flags;
14830
0
    g.DragDropAcceptIdCurr = g.DragDropTargetId;
14831
0
    g.DragDropAcceptIdCurrRectSurface = r_surface;
14832
    //IMGUI_DEBUG_LOG("AcceptDragDropPayload(): %08X: accept\n", g.DragDropTargetId);
14833
14834
    // Render default drop visuals
14835
0
    payload.Preview = was_accepted_previously;
14836
0
    flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)
14837
0
    const bool draw_target_rect = payload.Preview && !(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect);
14838
0
    if (draw_target_rect && g.DragDropTargetFullViewport != 0)
14839
0
    {
14840
0
        ImRect bb = g.DragDropTargetRect;
14841
0
        bb.Expand(-3.5f);
14842
0
        RenderDragDropTargetRectEx(GetForegroundDrawList(), bb);
14843
0
    }
14844
0
    else if (draw_target_rect)
14845
0
    {
14846
0
        RenderDragDropTargetRectForItem(r);
14847
0
    }
14848
14849
0
    g.DragDropAcceptFrameCount = g.FrameCount;
14850
0
    if ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) && g.DragDropMouseButton == -1)
14851
0
        payload.Delivery = was_accepted_previously && (g.DragDropSourceFrameCount < g.FrameCount);
14852
0
    else
14853
0
        payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()
14854
0
    if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))
14855
0
        return NULL;
14856
14857
0
    if (payload.Delivery)
14858
0
        IMGUI_DEBUG_LOG_ACTIVEID("[dragdrop] AcceptDragDropPayload(): 0x%08X: payload delivery\n", g.DragDropTargetId);
14859
0
    return &payload;
14860
0
}
14861
14862
// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.
14863
void ImGui::RenderDragDropTargetRectForItem(const ImRect& bb)
14864
0
{
14865
0
    ImGuiContext& g = *GImGui;
14866
0
    ImGuiWindow* window = g.CurrentWindow;
14867
0
    ImRect bb_display = bb;
14868
0
    bb_display.ClipWith(g.DragDropTargetClipRect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.
14869
0
    bb_display.Expand(3.5f);
14870
0
    bool push_clip_rect = !window->ClipRect.Contains(bb_display);
14871
0
    if (push_clip_rect)
14872
0
        window->DrawList->PushClipRectFullScreen();
14873
0
    RenderDragDropTargetRectEx(window->DrawList, bb_display);
14874
0
    if (push_clip_rect)
14875
0
        window->DrawList->PopClipRect();
14876
0
}
14877
14878
void ImGui::RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb)
14879
0
{
14880
0
    draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); // FIXME-DPI
14881
0
}
14882
14883
const ImGuiPayload* ImGui::GetDragDropPayload()
14884
0
{
14885
0
    ImGuiContext& g = *GImGui;
14886
0
    return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;
14887
0
}
14888
14889
void ImGui::EndDragDropTarget()
14890
0
{
14891
0
    ImGuiContext& g = *GImGui;
14892
0
    IM_ASSERT(g.DragDropActive);
14893
0
    IM_ASSERT(g.DragDropWithinTarget);
14894
0
    g.DragDropWithinTarget = false;
14895
14896
    // Clear drag and drop state payload right after delivery
14897
0
    if (g.DragDropPayload.Delivery)
14898
0
        ClearDragDrop();
14899
0
}
14900
14901
//-----------------------------------------------------------------------------
14902
// [SECTION] LOGGING/CAPTURING
14903
//-----------------------------------------------------------------------------
14904
// All text output from the interface can be captured into tty/file/clipboard.
14905
// By default, tree nodes are automatically opened during logging.
14906
//-----------------------------------------------------------------------------
14907
14908
// Pass text data straight to log (without being displayed)
14909
static inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)
14910
0
{
14911
0
    if (g.LogFile)
14912
0
    {
14913
0
        g.LogBuffer.Buf.resize(0);
14914
0
        g.LogBuffer.appendfv(fmt, args);
14915
0
        ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);
14916
0
    }
14917
0
    else
14918
0
    {
14919
0
        g.LogBuffer.appendfv(fmt, args);
14920
0
    }
14921
0
}
14922
14923
void ImGui::LogText(const char* fmt, ...)
14924
0
{
14925
0
    ImGuiContext& g = *GImGui;
14926
0
    if (!g.LogEnabled)
14927
0
        return;
14928
14929
0
    va_list args;
14930
0
    va_start(args, fmt);
14931
0
    LogTextV(g, fmt, args);
14932
0
    va_end(args);
14933
0
}
14934
14935
void ImGui::LogTextV(const char* fmt, va_list args)
14936
0
{
14937
0
    ImGuiContext& g = *GImGui;
14938
0
    if (!g.LogEnabled)
14939
0
        return;
14940
14941
0
    LogTextV(g, fmt, args);
14942
0
}
14943
14944
// Internal version that takes a position to decide on newline placement and pad items according to their depth.
14945
// We split text into individual lines to add current tree level padding
14946
// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.
14947
void ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)
14948
0
{
14949
0
    ImGuiContext& g = *GImGui;
14950
0
    ImGuiWindow* window = g.CurrentWindow;
14951
14952
0
    const char* prefix = g.LogNextPrefix;
14953
0
    const char* suffix = g.LogNextSuffix;
14954
0
    g.LogNextPrefix = g.LogNextSuffix = NULL;
14955
14956
0
    if (!text_end)
14957
0
        text_end = FindRenderedTextEnd(text, text_end);
14958
14959
0
    const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);
14960
0
    if (ref_pos)
14961
0
        g.LogLinePosY = ref_pos->y;
14962
0
    if (log_new_line)
14963
0
    {
14964
0
        LogText(IM_NEWLINE);
14965
0
        g.LogLineFirstItem = true;
14966
0
    }
14967
14968
0
    if (prefix)
14969
0
        LogRenderedText(ref_pos, prefix, prefix + ImStrlen(prefix)); // Calculate end ourself to ensure "##" are included here.
14970
14971
    // Re-adjust padding if we have popped out of our starting depth
14972
0
    if (g.LogDepthRef > window->DC.TreeDepth)
14973
0
        g.LogDepthRef = window->DC.TreeDepth;
14974
0
    const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);
14975
14976
0
    const char* text_remaining = text;
14977
0
    for (;;)
14978
0
    {
14979
        // Split the string. Each new line (after a '\n') is followed by indentation corresponding to the current depth of our log entry.
14980
        // We don't add a trailing \n yet to allow a subsequent item on the same line to be captured.
14981
0
        const char* line_start = text_remaining;
14982
0
        const char* line_end = ImStreolRange(line_start, text_end);
14983
0
        const bool is_last_line = (line_end == text_end);
14984
0
        if (line_start != line_end || !is_last_line)
14985
0
        {
14986
0
            const int line_length = (int)(line_end - line_start);
14987
0
            const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;
14988
0
            LogText("%*s%.*s", indentation, "", line_length, line_start);
14989
0
            g.LogLineFirstItem = false;
14990
0
            if (*line_end == '\n')
14991
0
            {
14992
0
                LogText(IM_NEWLINE);
14993
0
                g.LogLineFirstItem = true;
14994
0
            }
14995
0
        }
14996
0
        if (is_last_line)
14997
0
            break;
14998
0
        text_remaining = line_end + 1;
14999
0
    }
15000
15001
0
    if (suffix)
15002
0
        LogRenderedText(ref_pos, suffix, suffix + ImStrlen(suffix));
15003
0
}
15004
15005
// Start logging/capturing text output
15006
void ImGui::LogBegin(ImGuiLogFlags flags, int auto_open_depth)
15007
0
{
15008
0
    ImGuiContext& g = *GImGui;
15009
0
    ImGuiWindow* window = g.CurrentWindow;
15010
0
    IM_ASSERT(g.LogEnabled == false);
15011
0
    IM_ASSERT(g.LogFile == NULL && g.LogBuffer.empty());
15012
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiLogFlags_OutputMask_)); // Check that only 1 type flag is used
15013
15014
0
    g.LogEnabled = g.ItemUnclipByLog = true;
15015
0
    g.LogFlags = flags;
15016
0
    g.LogWindow = window;
15017
0
    g.LogNextPrefix = g.LogNextSuffix = NULL;
15018
0
    g.LogDepthRef = window->DC.TreeDepth;
15019
0
    g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);
15020
0
    g.LogLinePosY = FLT_MAX;
15021
0
    g.LogLineFirstItem = true;
15022
0
}
15023
15024
// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)
15025
void ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)
15026
241k
{
15027
241k
    ImGuiContext& g = *GImGui;
15028
241k
    g.LogNextPrefix = prefix;
15029
241k
    g.LogNextSuffix = suffix;
15030
241k
}
15031
15032
void ImGui::LogToTTY(int auto_open_depth)
15033
0
{
15034
0
    ImGuiContext& g = *GImGui;
15035
0
    if (g.LogEnabled)
15036
0
        return;
15037
0
    IM_UNUSED(auto_open_depth);
15038
0
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15039
0
    LogBegin(ImGuiLogFlags_OutputTTY, auto_open_depth);
15040
0
    g.LogFile = stdout;
15041
0
#endif
15042
0
}
15043
15044
// Start logging/capturing text output to given file
15045
void ImGui::LogToFile(int auto_open_depth, const char* filename)
15046
0
{
15047
0
    ImGuiContext& g = *GImGui;
15048
0
    if (g.LogEnabled)
15049
0
        return;
15050
15051
    // FIXME: We could probably open the file in text mode "at", however note that clipboard/buffer logging will still
15052
    // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.
15053
    // By opening the file in binary mode "ab" we have consistent output everywhere.
15054
0
    if (!filename)
15055
0
        filename = g.IO.LogFilename;
15056
0
    if (!filename || !filename[0])
15057
0
        return;
15058
0
    ImFileHandle f = ImFileOpen(filename, "ab");
15059
0
    if (!f)
15060
0
    {
15061
0
        IM_ASSERT(0);
15062
0
        return;
15063
0
    }
15064
15065
0
    LogBegin(ImGuiLogFlags_OutputFile, auto_open_depth);
15066
0
    g.LogFile = f;
15067
0
}
15068
15069
// Start logging/capturing text output to clipboard
15070
void ImGui::LogToClipboard(int auto_open_depth)
15071
0
{
15072
0
    ImGuiContext& g = *GImGui;
15073
0
    if (g.LogEnabled)
15074
0
        return;
15075
0
    LogBegin(ImGuiLogFlags_OutputClipboard, auto_open_depth);
15076
0
}
15077
15078
void ImGui::LogToBuffer(int auto_open_depth)
15079
0
{
15080
0
    ImGuiContext& g = *GImGui;
15081
0
    if (g.LogEnabled)
15082
0
        return;
15083
0
    LogBegin(ImGuiLogFlags_OutputBuffer, auto_open_depth);
15084
0
}
15085
15086
void ImGui::LogFinish()
15087
0
{
15088
0
    ImGuiContext& g = *GImGui;
15089
0
    if (!g.LogEnabled)
15090
0
        return;
15091
15092
0
    LogText(IM_NEWLINE);
15093
0
    switch (g.LogFlags & ImGuiLogFlags_OutputMask_)
15094
0
    {
15095
0
    case ImGuiLogFlags_OutputTTY:
15096
0
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15097
0
        fflush(g.LogFile);
15098
0
#endif
15099
0
        break;
15100
0
    case ImGuiLogFlags_OutputFile:
15101
0
        ImFileClose(g.LogFile);
15102
0
        break;
15103
0
    case ImGuiLogFlags_OutputBuffer:
15104
0
        break;
15105
0
    case ImGuiLogFlags_OutputClipboard:
15106
0
        if (!g.LogBuffer.empty())
15107
0
            SetClipboardText(g.LogBuffer.begin());
15108
0
        break;
15109
0
    default:
15110
0
        IM_ASSERT(0);
15111
0
        break;
15112
0
    }
15113
15114
0
    g.LogEnabled = g.ItemUnclipByLog = false;
15115
0
    g.LogFlags = ImGuiLogFlags_None;
15116
0
    g.LogFile = NULL;
15117
0
    g.LogBuffer.clear();
15118
0
}
15119
15120
// Helper to display logging buttons
15121
// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)
15122
void ImGui::LogButtons()
15123
0
{
15124
0
    ImGuiContext& g = *GImGui;
15125
15126
0
    PushID("LogButtons");
15127
0
#ifndef IMGUI_DISABLE_TTY_FUNCTIONS
15128
0
    const bool log_to_tty = Button("Log To TTY"); SameLine();
15129
#else
15130
    const bool log_to_tty = false;
15131
#endif
15132
0
    const bool log_to_file = Button("Log To File"); SameLine();
15133
0
    const bool log_to_clipboard = Button("Log To Clipboard"); SameLine();
15134
0
    PushItemFlag(ImGuiItemFlags_NoTabStop, true);
15135
0
    SetNextItemWidth(80.0f);
15136
0
    SliderInt("Default Depth", &g.LogDepthToExpandDefault, 0, 9, NULL);
15137
0
    PopItemFlag();
15138
0
    PopID();
15139
15140
    // Start logging at the end of the function so that the buttons don't appear in the log
15141
0
    if (log_to_tty)
15142
0
        LogToTTY();
15143
0
    if (log_to_file)
15144
0
        LogToFile();
15145
0
    if (log_to_clipboard)
15146
0
        LogToClipboard();
15147
0
}
15148
15149
//-----------------------------------------------------------------------------
15150
// [SECTION] SETTINGS
15151
//-----------------------------------------------------------------------------
15152
// - UpdateSettings() [Internal]
15153
// - MarkIniSettingsDirty() [Internal]
15154
// - FindSettingsHandler() [Internal]
15155
// - ClearIniSettings() [Internal]
15156
// - LoadIniSettingsFromDisk()
15157
// - LoadIniSettingsFromMemory()
15158
// - SaveIniSettingsToDisk()
15159
// - SaveIniSettingsToMemory()
15160
//-----------------------------------------------------------------------------
15161
// - CreateNewWindowSettings() [Internal]
15162
// - FindWindowSettingsByID() [Internal]
15163
// - FindWindowSettingsByWindow() [Internal]
15164
// - ClearWindowSettings() [Internal]
15165
// - WindowSettingsHandler_***() [Internal]
15166
//-----------------------------------------------------------------------------
15167
15168
// Called by NewFrame()
15169
void ImGui::UpdateSettings()
15170
80.5k
{
15171
    // Load settings on first frame (if not explicitly loaded manually before)
15172
80.5k
    ImGuiContext& g = *GImGui;
15173
80.5k
    if (!g.SettingsLoaded)
15174
1
    {
15175
1
        IM_ASSERT(g.SettingsWindows.empty());
15176
1
        if (g.IO.IniFilename)
15177
1
            LoadIniSettingsFromDisk(g.IO.IniFilename);
15178
1
        g.SettingsLoaded = true;
15179
1
    }
15180
15181
    // Save settings (with a delay after the last modification, so we don't spam disk too much)
15182
80.5k
    if (g.SettingsDirtyTimer > 0.0f)
15183
0
    {
15184
0
        g.SettingsDirtyTimer -= g.IO.DeltaTime;
15185
0
        if (g.SettingsDirtyTimer <= 0.0f)
15186
0
        {
15187
0
            if (g.IO.IniFilename != NULL)
15188
0
                SaveIniSettingsToDisk(g.IO.IniFilename);
15189
0
            else
15190
0
                g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.
15191
0
            g.SettingsDirtyTimer = 0.0f;
15192
0
        }
15193
0
    }
15194
80.5k
}
15195
15196
void ImGui::MarkIniSettingsDirty()
15197
0
{
15198
0
    ImGuiContext& g = *GImGui;
15199
0
    if (g.SettingsDirtyTimer <= 0.0f)
15200
0
        g.SettingsDirtyTimer = g.IO.IniSavingRate;
15201
0
}
15202
15203
void ImGui::MarkIniSettingsDirty(ImGuiWindow* window)
15204
662
{
15205
662
    ImGuiContext& g = *GImGui;
15206
662
    if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))
15207
0
        if (g.SettingsDirtyTimer <= 0.0f)
15208
0
            g.SettingsDirtyTimer = g.IO.IniSavingRate;
15209
662
}
15210
15211
void ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)
15212
2
{
15213
2
    ImGuiContext& g = *GImGui;
15214
2
    IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);
15215
2
    g.SettingsHandlers.push_back(*handler);
15216
2
}
15217
15218
void ImGui::RemoveSettingsHandler(const char* type_name)
15219
0
{
15220
0
    ImGuiContext& g = *GImGui;
15221
0
    if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))
15222
0
        g.SettingsHandlers.erase(handler);
15223
0
}
15224
15225
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
15226
31
{
15227
31
    ImGuiContext& g = *GImGui;
15228
31
    const ImGuiID type_hash = ImHashStr(type_name);
15229
31
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15230
56
        if (handler.TypeHash == type_hash)
15231
29
            return &handler;
15232
2
    return NULL;
15233
31
}
15234
15235
// Clear all settings (windows, tables, docking etc.)
15236
void ImGui::ClearIniSettings()
15237
0
{
15238
0
    ImGuiContext& g = *GImGui;
15239
0
    g.SettingsIniData.clear();
15240
0
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15241
0
        if (handler.ClearAllFn != NULL)
15242
0
            handler.ClearAllFn(&g, &handler);
15243
0
}
15244
15245
void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
15246
1
{
15247
1
    size_t file_data_size = 0;
15248
1
    char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size);
15249
1
    if (!file_data)
15250
0
        return;
15251
1
    if (file_data_size > 0)
15252
1
        LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);
15253
1
    IM_FREE(file_data);
15254
1
}
15255
15256
// Zero-tolerance, no error reporting, cheap .ini parsing
15257
// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!
15258
void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
15259
1
{
15260
1
    ImGuiContext& g = *GImGui;
15261
1
    IM_ASSERT(g.Initialized);
15262
    //IM_ASSERT(!g.WithinFrameScope && "Cannot be called between NewFrame() and EndFrame()");
15263
    //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);
15264
15265
    // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).
15266
    // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..
15267
1
    if (ini_size == 0)
15268
0
        ini_size = ImStrlen(ini_data);
15269
1
    g.SettingsIniData.Buf.resize((int)ini_size + 1);
15270
1
    char* const buf = g.SettingsIniData.Buf.Data;
15271
1
    char* const buf_end = buf + ini_size;
15272
1
    memcpy(buf, ini_data, ini_size);
15273
1
    buf_end[0] = 0;
15274
15275
    // Call pre-read handlers
15276
    // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)
15277
1
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15278
2
        if (handler.ReadInitFn != NULL)
15279
0
            handler.ReadInitFn(&g, &handler);
15280
15281
1
    void* entry_data = NULL;
15282
1
    ImGuiSettingsHandler* entry_handler = NULL;
15283
15284
1
    char* line_end = NULL;
15285
89
    for (char* line = buf; line < buf_end; line = line_end + 1)
15286
88
    {
15287
        // Skip new lines markers, then find end of the line
15288
117
        while (*line == '\n' || *line == '\r')
15289
29
            line++;
15290
88
        line_end = line;
15291
1.53k
        while (line_end < buf_end && *line_end != '\n' && *line_end != '\r')
15292
1.44k
            line_end++;
15293
88
        line_end[0] = 0;
15294
88
        if (line[0] == ';')
15295
0
            continue;
15296
88
        if (line[0] == '[' && line_end > line && line_end[-1] == ']')
15297
29
        {
15298
            // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.
15299
29
            line_end[-1] = 0;
15300
29
            const char* name_end = line_end - 1;
15301
29
            const char* type_start = line + 1;
15302
29
            char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');
15303
29
            const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;
15304
29
            if (!type_end || !name_start)
15305
0
                continue;
15306
29
            *type_end = 0; // Overwrite first ']'
15307
29
            name_start++;  // Skip second '['
15308
29
            entry_handler = FindSettingsHandler(type_start);
15309
29
            entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;
15310
29
        }
15311
59
        else if (entry_handler != NULL && entry_data != NULL)
15312
59
        {
15313
            // Let type handler parse the line
15314
59
            entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);
15315
59
        }
15316
88
    }
15317
1
    g.SettingsLoaded = true;
15318
15319
    // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)
15320
1
    memcpy(buf, ini_data, ini_size);
15321
15322
    // Call post-read handlers
15323
1
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15324
2
        if (handler.ApplyAllFn != NULL)
15325
2
            handler.ApplyAllFn(&g, &handler);
15326
1
}
15327
15328
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
15329
1
{
15330
1
    ImGuiContext& g = *GImGui;
15331
1
    g.SettingsDirtyTimer = 0.0f;
15332
1
    if (!ini_filename)
15333
0
        return;
15334
15335
1
    size_t ini_data_size = 0;
15336
1
    const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);
15337
1
    ImFileHandle f = ImFileOpen(ini_filename, "wt");
15338
1
    if (!f)
15339
0
        return;
15340
1
    ImFileWrite(ini_data, sizeof(char), ini_data_size, f);
15341
1
    ImFileClose(f);
15342
1
}
15343
15344
// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer
15345
const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
15346
1
{
15347
1
    ImGuiContext& g = *GImGui;
15348
1
    g.SettingsDirtyTimer = 0.0f;
15349
1
    g.SettingsIniData.Buf.resize(0);
15350
1
    g.SettingsIniData.Buf.push_back(0);
15351
1
    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
15352
2
        handler.WriteAllFn(&g, &handler, &g.SettingsIniData);
15353
1
    if (out_size)
15354
1
        *out_size = (size_t)g.SettingsIniData.size();
15355
1
    return g.SettingsIniData.c_str();
15356
1
}
15357
15358
ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
15359
3
{
15360
3
    ImGuiContext& g = *GImGui;
15361
15362
    // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.
15363
3
    if (g.IO.ConfigDebugIniSettings == false)
15364
3
        name = ImHashSkipUncontributingPrefix(name);
15365
3
    const size_t name_len = ImStrlen(name);
15366
15367
    // Allocate chunk
15368
3
    const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;
15369
3
    ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);
15370
3
    IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();
15371
3
    settings->ID = ImHashStr(name, name_len);
15372
3
    memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator
15373
15374
3
    return settings;
15375
3
}
15376
15377
// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.
15378
// This is called once per window .ini entry + once per newly instantiated window.
15379
ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)
15380
4
{
15381
4
    ImGuiContext& g = *GImGui;
15382
7
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15383
4
        if (settings->ID == id && !settings->WantDelete)
15384
1
            return settings;
15385
3
    return NULL;
15386
4
}
15387
15388
// This is faster if you are holding on a Window already as we don't need to perform a search.
15389
ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)
15390
2
{
15391
2
    ImGuiContext& g = *GImGui;
15392
2
    if (window->SettingsOffset != -1)
15393
1
        return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);
15394
1
    return FindWindowSettingsByID(window->ID);
15395
2
}
15396
15397
// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.
15398
void ImGui::ClearWindowSettings(const char* name)
15399
0
{
15400
    //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name);
15401
0
    ImGuiWindow* window = FindWindowByName(name);
15402
0
    if (window != NULL)
15403
0
    {
15404
0
        window->Flags |= ImGuiWindowFlags_NoSavedSettings;
15405
0
        InitOrLoadWindowSettings(window, NULL);
15406
0
    }
15407
0
    if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))
15408
0
        settings->WantDelete = true;
15409
0
}
15410
15411
static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15412
0
{
15413
0
    ImGuiContext& g = *ctx;
15414
0
    for (ImGuiWindow* window : g.Windows)
15415
0
        window->SettingsOffset = -1;
15416
0
    g.SettingsWindows.clear();
15417
0
}
15418
15419
static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
15420
3
{
15421
3
    ImGuiID id = ImHashStr(name);
15422
3
    ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);
15423
3
    if (settings)
15424
0
        *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry
15425
3
    else
15426
3
        settings = ImGui::CreateNewWindowSettings(name);
15427
3
    settings->ID = id;
15428
3
    settings->WantApply = true;
15429
3
    return (void*)settings;
15430
3
}
15431
15432
static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
15433
6
{
15434
6
    ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
15435
6
    int x, y;
15436
6
    int i;
15437
6
    if (sscanf(line, "Pos=%i,%i", &x, &y) == 2)         { settings->Pos = ImVec2ih((short)x, (short)y); }
15438
3
    else if (sscanf(line, "Size=%i,%i", &x, &y) == 2)   { settings->Size = ImVec2ih((short)x, (short)y); }
15439
0
    else if (sscanf(line, "Collapsed=%d", &i) == 1)     { settings->Collapsed = (i != 0); }
15440
0
    else if (sscanf(line, "IsChild=%d", &i) == 1)       { settings->IsChild = (i != 0); }
15441
6
}
15442
15443
// Apply to existing windows (if any)
15444
static void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
15445
1
{
15446
1
    ImGuiContext& g = *ctx;
15447
4
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15448
3
        if (settings->WantApply)
15449
3
        {
15450
3
            if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))
15451
0
                ApplyWindowSettings(window, settings);
15452
3
            settings->WantApply = false;
15453
3
        }
15454
1
}
15455
15456
static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
15457
1
{
15458
    // Gather data from windows that were active during this session
15459
    // (if a window wasn't opened in this session we preserve its settings)
15460
1
    ImGuiContext& g = *ctx;
15461
1
    for (ImGuiWindow* window : g.Windows)
15462
3
    {
15463
3
        if (window->Flags & ImGuiWindowFlags_NoSavedSettings)
15464
2
            continue;
15465
15466
1
        ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);
15467
1
        if (!settings)
15468
0
        {
15469
0
            settings = ImGui::CreateNewWindowSettings(window->Name);
15470
0
            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);
15471
0
        }
15472
1
        IM_ASSERT(settings->ID == window->ID);
15473
1
        settings->Pos = ImVec2ih(window->Pos);
15474
1
        settings->Size = ImVec2ih(window->SizeFull);
15475
1
        settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;
15476
1
        settings->Collapsed = window->Collapsed;
15477
1
        settings->WantDelete = false;
15478
1
    }
15479
15480
    // Write to text buffer
15481
1
    buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve
15482
4
    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
15483
3
    {
15484
3
        if (settings->WantDelete)
15485
0
            continue;
15486
3
        const char* settings_name = settings->GetName();
15487
3
        buf->appendf("[%s][%s]\n", handler->TypeName, settings_name);
15488
3
        if (settings->IsChild)
15489
0
        {
15490
0
            buf->appendf("IsChild=1\n");
15491
0
            buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15492
0
        }
15493
3
        else
15494
3
        {
15495
3
            buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y);
15496
3
            buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y);
15497
3
            if (settings->Collapsed)
15498
0
                buf->appendf("Collapsed=1\n");
15499
3
        }
15500
3
        buf->append("\n");
15501
3
    }
15502
1
}
15503
15504
//-----------------------------------------------------------------------------
15505
// [SECTION] LOCALIZATION
15506
//-----------------------------------------------------------------------------
15507
15508
void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)
15509
1
{
15510
1
    ImGuiContext& g = *GImGui;
15511
11
    for (int n = 0; n < count; n++)
15512
10
        g.LocalizationTable[entries[n].Key] = entries[n].Text;
15513
1
}
15514
15515
//-----------------------------------------------------------------------------
15516
// [SECTION] VIEWPORTS, PLATFORM WINDOWS
15517
//-----------------------------------------------------------------------------
15518
// - GetMainViewport()
15519
// - SetWindowViewport() [Internal]
15520
// - ScaleWindowsInViewport() [Internal]
15521
// - UpdateViewportsNewFrame() [Internal]
15522
// (this section is more complete in the 'docking' branch)
15523
//-----------------------------------------------------------------------------
15524
15525
void ImGuiPlatformIO::ClearPlatformHandlers()
15526
1
{
15527
1
    Platform_GetClipboardTextFn = NULL;
15528
1
    Platform_SetClipboardTextFn = NULL;
15529
1
    Platform_ClipboardUserData = NULL;
15530
1
    Platform_OpenInShellFn = NULL;
15531
1
    Platform_OpenInShellUserData = NULL;
15532
1
    Platform_SetImeDataFn = NULL;
15533
1
    Platform_ImeUserData = NULL;
15534
1
}
15535
15536
void ImGuiPlatformIO::ClearRendererHandlers()
15537
1
{
15538
1
    Renderer_TextureMaxWidth = Renderer_TextureMaxHeight = 0;
15539
1
    Renderer_RenderState = NULL;
15540
1
}
15541
15542
ImGuiViewport* ImGui::GetMainViewport()
15543
406k
{
15544
406k
    ImGuiContext& g = *GImGui;
15545
406k
    return g.Viewports[0];
15546
406k
}
15547
15548
void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)
15549
161k
{
15550
161k
    window->Viewport = viewport;
15551
161k
}
15552
15553
static void ScaleWindow(ImGuiWindow* window, float scale)
15554
0
{
15555
0
    ImVec2 origin = window->Viewport->Pos;
15556
0
    window->Pos = ImFloor((window->Pos - origin) * scale + origin);
15557
0
    window->Size = ImTrunc(window->Size * scale);
15558
0
    window->SizeFull = ImTrunc(window->SizeFull * scale);
15559
0
    window->ContentSize = ImTrunc(window->ContentSize * scale);
15560
0
}
15561
15562
// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!)
15563
void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale)
15564
0
{
15565
0
    ImGuiContext& g = *GImGui;
15566
0
    for (ImGuiWindow* window : g.Windows)
15567
0
        if (window->Viewport == viewport)
15568
0
            ScaleWindow(window, scale);
15569
0
}
15570
15571
// Update viewports and monitor infos
15572
static void ImGui::UpdateViewportsNewFrame()
15573
80.5k
{
15574
80.5k
    ImGuiContext& g = *GImGui;
15575
80.5k
    IM_ASSERT(g.Viewports.Size == 1);
15576
15577
    // Update main viewport with current platform position.
15578
    // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.
15579
80.5k
    ImGuiViewportP* main_viewport = g.Viewports[0];
15580
80.5k
    main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;
15581
80.5k
    main_viewport->Pos = ImVec2(0.0f, 0.0f);
15582
80.5k
    main_viewport->Size = g.IO.DisplaySize;
15583
80.5k
    main_viewport->FramebufferScale = g.IO.DisplayFramebufferScale;
15584
80.5k
    IM_ASSERT(main_viewport->FramebufferScale.x > 0.0f && main_viewport->FramebufferScale.y > 0.0f);
15585
15586
80.5k
    for (ImGuiViewportP* viewport : g.Viewports)
15587
80.5k
    {
15588
        // Lock down space taken by menu bars and status bars
15589
        // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc.
15590
80.5k
        viewport->WorkInsetMin = viewport->BuildWorkInsetMin;
15591
80.5k
        viewport->WorkInsetMax = viewport->BuildWorkInsetMax;
15592
80.5k
        viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f);
15593
80.5k
        viewport->UpdateWorkRect();
15594
80.5k
    }
15595
80.5k
}
15596
15597
//-----------------------------------------------------------------------------
15598
// [SECTION] DOCKING
15599
//-----------------------------------------------------------------------------
15600
15601
// (this section is filled in the 'docking' branch)
15602
15603
15604
//-----------------------------------------------------------------------------
15605
// [SECTION] PLATFORM DEPENDENT HELPERS
15606
//-----------------------------------------------------------------------------
15607
// - Default clipboard handlers
15608
// - Default shell function handlers
15609
// - Default IME handlers
15610
//-----------------------------------------------------------------------------
15611
15612
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)
15613
15614
#ifdef _MSC_VER
15615
#pragma comment(lib, "user32")
15616
#pragma comment(lib, "kernel32")
15617
#endif
15618
15619
// Win32 clipboard implementation
15620
// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()
15621
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15622
{
15623
    ImGuiContext& g = *ctx;
15624
    g.ClipboardHandlerData.clear();
15625
    if (!::OpenClipboard(NULL))
15626
        return NULL;
15627
    HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);
15628
    if (wbuf_handle == NULL)
15629
    {
15630
        ::CloseClipboard();
15631
        return NULL;
15632
    }
15633
    if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))
15634
    {
15635
        int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);
15636
        g.ClipboardHandlerData.resize(buf_len);
15637
        ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);
15638
    }
15639
    ::GlobalUnlock(wbuf_handle);
15640
    ::CloseClipboard();
15641
    return g.ClipboardHandlerData.Data;
15642
}
15643
15644
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
15645
{
15646
    if (!::OpenClipboard(NULL))
15647
        return;
15648
    const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);
15649
    HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));
15650
    if (wbuf_handle == NULL)
15651
    {
15652
        ::CloseClipboard();
15653
        return;
15654
    }
15655
    WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);
15656
    ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);
15657
    ::GlobalUnlock(wbuf_handle);
15658
    ::EmptyClipboard();
15659
    if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)
15660
        ::GlobalFree(wbuf_handle);
15661
    ::CloseClipboard();
15662
}
15663
15664
#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)
15665
15666
#include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file
15667
static PasteboardRef main_clipboard = 0;
15668
15669
// OSX clipboard implementation
15670
// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!
15671
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text)
15672
{
15673
    if (!main_clipboard)
15674
        PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15675
    PasteboardClear(main_clipboard);
15676
    CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, ImStrlen(text));
15677
    if (cf_data)
15678
    {
15679
        PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0);
15680
        CFRelease(cf_data);
15681
    }
15682
}
15683
15684
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15685
{
15686
    ImGuiContext& g = *ctx;
15687
    if (!main_clipboard)
15688
        PasteboardCreate(kPasteboardClipboard, &main_clipboard);
15689
    PasteboardSynchronize(main_clipboard);
15690
15691
    ItemCount item_count = 0;
15692
    PasteboardGetItemCount(main_clipboard, &item_count);
15693
    for (ItemCount i = 0; i < item_count; i++)
15694
    {
15695
        PasteboardItemID item_id = 0;
15696
        PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);
15697
        CFArrayRef flavor_type_array = 0;
15698
        PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);
15699
        for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)
15700
        {
15701
            CFDataRef cf_data;
15702
            if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr)
15703
            {
15704
                g.ClipboardHandlerData.clear();
15705
                int length = (int)CFDataGetLength(cf_data);
15706
                g.ClipboardHandlerData.resize(length + 1);
15707
                CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);
15708
                g.ClipboardHandlerData[length] = 0;
15709
                CFRelease(cf_data);
15710
                return g.ClipboardHandlerData.Data;
15711
            }
15712
        }
15713
    }
15714
    return NULL;
15715
}
15716
15717
#else
15718
15719
// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.
15720
static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx)
15721
0
{
15722
0
    ImGuiContext& g = *ctx;
15723
0
    return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();
15724
0
}
15725
15726
static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext* ctx, const char* text)
15727
0
{
15728
0
    ImGuiContext& g = *ctx;
15729
0
    g.ClipboardHandlerData.clear();
15730
0
    const char* text_end = text + ImStrlen(text);
15731
0
    g.ClipboardHandlerData.resize((int)(text_end - text) + 1);
15732
0
    memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));
15733
0
    g.ClipboardHandlerData[(int)(text_end - text)] = 0;
15734
0
}
15735
15736
#endif // Default clipboard handlers
15737
15738
//-----------------------------------------------------------------------------
15739
15740
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15741
#if defined(__APPLE__) && TARGET_OS_IPHONE
15742
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15743
#endif
15744
#if defined(__3DS__)
15745
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15746
#endif
15747
#if defined(_WIN32) && defined(IMGUI_DISABLE_WIN32_FUNCTIONS)
15748
#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15749
#endif
15750
#endif // #ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15751
15752
#ifndef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
15753
#ifdef _WIN32
15754
#include <shellapi.h>   // ShellExecuteA()
15755
#ifdef _MSC_VER
15756
#pragma comment(lib, "shell32")
15757
#endif
15758
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
15759
{
15760
    const int path_wsize = ::MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
15761
    ImVector<wchar_t> path_wbuf;
15762
    path_wbuf.resize(path_wsize);
15763
    ::MultiByteToWideChar(CP_UTF8, 0, path, -1, path_wbuf.Data, path_wsize);
15764
    return (INT_PTR)::ShellExecuteW(NULL, L"open", path_wbuf.Data, NULL, NULL, SW_SHOWDEFAULT) > 32;
15765
}
15766
#else
15767
#include <sys/wait.h>
15768
#include <unistd.h>
15769
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char* path)
15770
0
{
15771
0
#if defined(__APPLE__)
15772
0
    const char* args[] { "open", "--", path, NULL };
15773
#else
15774
    const char* args[] { "xdg-open", path, NULL };
15775
#endif
15776
0
    pid_t pid = fork();
15777
0
    if (pid < 0)
15778
0
        return false;
15779
0
    if (!pid)
15780
0
    {
15781
0
        execvp(args[0], const_cast<char **>(args));
15782
0
        exit(-1);
15783
0
    }
15784
0
    else
15785
0
    {
15786
0
        int status;
15787
0
        waitpid(pid, &status, 0);
15788
0
        return WEXITSTATUS(status) == 0;
15789
0
    }
15790
0
}
15791
#endif
15792
#else
15793
static bool Platform_OpenInShellFn_DefaultImpl(ImGuiContext*, const char*) { return false; }
15794
#endif // Default shell handlers
15795
15796
//-----------------------------------------------------------------------------
15797
15798
// Win32 API IME support (for Asian languages, etc.)
15799
#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)
15800
15801
#include <imm.h>
15802
#ifdef _MSC_VER
15803
#pragma comment(lib, "imm32")
15804
#endif
15805
15806
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
15807
{
15808
    // Notify OS Input Method Editor of text input position
15809
    HWND hwnd = (HWND)viewport->PlatformHandleRaw;
15810
    if (hwnd == 0)
15811
        return;
15812
15813
    //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);
15814
    if (HIMC himc = ::ImmGetContext(hwnd))
15815
    {
15816
        COMPOSITIONFORM composition_form = {};
15817
        composition_form.ptCurrentPos.x = (LONG)data->InputPos.x;
15818
        composition_form.ptCurrentPos.y = (LONG)data->InputPos.y;
15819
        composition_form.dwStyle = CFS_FORCE_POSITION;
15820
        ::ImmSetCompositionWindow(himc, &composition_form);
15821
        CANDIDATEFORM candidate_form = {};
15822
        candidate_form.dwStyle = CFS_CANDIDATEPOS;
15823
        candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x;
15824
        candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y;
15825
        ::ImmSetCandidateWindow(himc, &candidate_form);
15826
        ::ImmReleaseContext(hwnd, himc);
15827
    }
15828
}
15829
15830
#else
15831
15832
0
static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData*) {}
15833
15834
#endif // Default IME handlers
15835
15836
//-----------------------------------------------------------------------------
15837
// [SECTION] METRICS/DEBUGGER WINDOW
15838
//-----------------------------------------------------------------------------
15839
// - MetricsHelpMarker() [Internal]
15840
// - DebugRenderViewportThumbnail() [Internal]
15841
// - RenderViewportsThumbnails() [Internal]
15842
// - DebugRenderKeyboardPreview() [Internal]
15843
// - DebugTextEncoding()
15844
// - DebugFlashStyleColorStop() [Internal]
15845
// - DebugFlashStyleColor()
15846
// - UpdateDebugToolFlashStyleColor() [Internal]
15847
// - ShowFontAtlas() [Internal but called by Demo!]
15848
// - DebugNodeTexture() [Internal]
15849
// - ShowMetricsWindow()
15850
// - DebugNodeColumns() [Internal]
15851
// - DebugNodeDrawList() [Internal]
15852
// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]
15853
// - DebugNodeFont() [Internal]
15854
// - DebugNodeFontGlyph() [Internal]
15855
// - DebugNodeStorage() [Internal]
15856
// - DebugNodeTabBar() [Internal]
15857
// - DebugNodeViewport() [Internal]
15858
// - DebugNodeWindow() [Internal]
15859
// - DebugNodeWindowSettings() [Internal]
15860
// - DebugNodeWindowsList() [Internal]
15861
// - DebugNodeWindowsListByBeginStackParent() [Internal]
15862
// - ShowFontSelector()
15863
//-----------------------------------------------------------------------------
15864
15865
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
15866
// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.
15867
static void MetricsHelpMarker(const char* desc)
15868
0
{
15869
0
    ImGui::TextDisabled("(?)");
15870
0
    if (ImGui::BeginItemTooltip())
15871
0
    {
15872
0
        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
15873
0
        ImGui::TextUnformatted(desc);
15874
0
        ImGui::PopTextWrapPos();
15875
0
        ImGui::EndTooltip();
15876
0
    }
15877
0
}
15878
#endif
15879
15880
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
15881
15882
void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)
15883
0
{
15884
0
    ImGuiContext& g = *GImGui;
15885
0
    ImGuiWindow* window = g.CurrentWindow;
15886
15887
0
    ImVec2 scale = bb.GetSize() / viewport->Size;
15888
0
    ImVec2 off = bb.Min - viewport->Pos * scale;
15889
0
    float alpha_mul = 1.0f;
15890
0
    window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));
15891
0
    for (ImGuiWindow* thumb_window : g.Windows)
15892
0
    {
15893
0
        if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))
15894
0
            continue;
15895
15896
0
        ImRect thumb_r = thumb_window->Rect();
15897
0
        ImRect title_r = thumb_window->TitleBarRect();
15898
0
        thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off +  thumb_r.Max * scale));
15899
0
        title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off +  ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height
15900
0
        thumb_r.ClipWithFull(bb);
15901
0
        title_r.ClipWithFull(bb);
15902
0
        const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);
15903
0
        window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));
15904
0
        window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));
15905
0
        window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
15906
0
        window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));
15907
0
    }
15908
0
    draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));
15909
0
    if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID)
15910
0
        window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));
15911
0
}
15912
15913
static void RenderViewportsThumbnails()
15914
0
{
15915
0
    ImGuiContext& g = *GImGui;
15916
0
    ImGuiWindow* window = g.CurrentWindow;
15917
15918
0
    float SCALE = 1.0f / 8.0f;
15919
0
    ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size);
15920
0
    ImVec2 p = window->DC.CursorPos;
15921
0
    ImVec2 off = p - bb_full.Min * SCALE;
15922
15923
    // Draw viewports
15924
0
    for (ImGuiViewportP* viewport : g.Viewports)
15925
0
    {
15926
0
        ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);
15927
0
        ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);
15928
0
    }
15929
0
    ImGui::Dummy(bb_full.GetSize() * SCALE);
15930
0
}
15931
15932
// Draw an arbitrary US keyboard layout to visualize translated keys
15933
void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)
15934
0
{
15935
0
    const float scale = ImGui::GetFontSize() / 13.0f;
15936
0
    const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale;
15937
0
    const float  key_rounding = 3.0f * scale;
15938
0
    const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale;
15939
0
    const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale;
15940
0
    const float  key_face_rounding = 2.0f * scale;
15941
0
    const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale;
15942
0
    const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);
15943
0
    const float  key_row_offset = 9.0f * scale;
15944
15945
0
    ImVec2 board_min = GetCursorScreenPos();
15946
0
    ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);
15947
0
    ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);
15948
15949
0
    struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };
15950
0
    const KeyLayoutData keys_to_display[] =
15951
0
    {
15952
0
        { 0, 0, "", ImGuiKey_Tab },      { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R },
15953
0
        { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F },
15954
0
        { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V }
15955
0
    };
15956
15957
    // Elements rendered manually via ImDrawList API are not clipped automatically.
15958
    // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.
15959
0
    Dummy(board_max - board_min);
15960
0
    if (!IsItemVisible())
15961
0
        return;
15962
0
    draw_list->PushClipRect(board_min, board_max, true);
15963
0
    for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)
15964
0
    {
15965
0
        const KeyLayoutData* key_data = &keys_to_display[n];
15966
0
        ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);
15967
0
        ImVec2 key_max = key_min + key_size;
15968
0
        draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);
15969
0
        draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);
15970
0
        ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);
15971
0
        ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);
15972
0
        draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);
15973
0
        draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);
15974
0
        ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);
15975
0
        draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);
15976
0
        if (IsKeyDown(key_data->Key))
15977
0
            draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);
15978
0
    }
15979
0
    draw_list->PopClipRect();
15980
0
}
15981
15982
// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.
15983
void ImGui::DebugTextEncoding(const char* str)
15984
0
{
15985
0
    Text("Text: \"%s\"", str);
15986
0
    if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))
15987
0
        return;
15988
0
    TableSetupColumn("Offset");
15989
0
    TableSetupColumn("UTF-8");
15990
0
    TableSetupColumn("Glyph");
15991
0
    TableSetupColumn("Codepoint");
15992
0
    TableHeadersRow();
15993
0
    for (const char* p = str; *p != 0; )
15994
0
    {
15995
0
        unsigned int c;
15996
0
        const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);
15997
0
        TableNextColumn();
15998
0
        Text("%d", (int)(p - str));
15999
0
        TableNextColumn();
16000
0
        for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)
16001
0
        {
16002
0
            if (byte_index > 0)
16003
0
                SameLine();
16004
0
            Text("0x%02X", (int)(unsigned char)p[byte_index]);
16005
0
        }
16006
0
        TableNextColumn();
16007
0
        TextUnformatted(p, p + c_utf8_len);
16008
0
        if (!GetFont()->IsGlyphInFont((ImWchar)c))
16009
0
        {
16010
0
            SameLine();
16011
0
            TextUnformatted("[missing]");
16012
0
        }
16013
0
        TableNextColumn();
16014
0
        Text("U+%04X", (int)c);
16015
0
        p += c_utf8_len;
16016
0
    }
16017
0
    EndTable();
16018
0
}
16019
16020
static void DebugFlashStyleColorStop()
16021
0
{
16022
0
    ImGuiContext& g = *GImGui;
16023
0
    if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT)
16024
0
        g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup;
16025
0
    g.DebugFlashStyleColorIdx = ImGuiCol_COUNT;
16026
0
}
16027
16028
// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls.
16029
void ImGui::DebugFlashStyleColor(ImGuiCol idx)
16030
0
{
16031
0
    ImGuiContext& g = *GImGui;
16032
0
    DebugFlashStyleColorStop();
16033
0
    g.DebugFlashStyleColorTime = 0.5f;
16034
0
    g.DebugFlashStyleColorIdx = idx;
16035
0
    g.DebugFlashStyleColorBackup = g.Style.Colors[idx];
16036
0
}
16037
16038
void ImGui::UpdateDebugToolFlashStyleColor()
16039
80.5k
{
16040
80.5k
    ImGuiContext& g = *GImGui;
16041
80.5k
    if (g.DebugFlashStyleColorTime <= 0.0f)
16042
80.5k
        return;
16043
0
    ColorConvertHSVtoRGB(ImCos(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z);
16044
0
    g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f;
16045
0
    if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f)
16046
0
        DebugFlashStyleColorStop();
16047
0
}
16048
16049
static const char* FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id)
16050
0
{
16051
0
    union { void* ptr; int integer; } tex_id_opaque;
16052
0
    memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id)));
16053
0
    if (sizeof(tex_id) >= sizeof(void*))
16054
0
        ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr);
16055
0
    else
16056
0
        ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer);
16057
0
    return buf;
16058
0
}
16059
16060
static const char* FormatTextureRefForDebugDisplay(char* buf, int buf_size, ImTextureRef tex_ref)
16061
0
{
16062
0
    char* buf_end = buf + buf_size;
16063
0
    if (tex_ref._TexData != NULL)
16064
0
        buf += ImFormatString(buf, buf_end - buf, "#%03d: ", tex_ref._TexData->UniqueID);
16065
0
    return FormatTextureIDForDebugDisplay(buf, (int)(buf_end - buf), tex_ref.GetTexID()); // Calling TexRef::GetTexID() to avoid assert of cmd->GetTexID()
16066
0
}
16067
16068
#ifdef IMGUI_ENABLE_FREETYPE
16069
namespace ImGuiFreeType { IMGUI_API const ImFontLoader* GetFontLoader(); IMGUI_API bool DebugEditFontLoaderFlags(unsigned int* p_font_builder_flags); }
16070
#endif
16071
16072
// [DEBUG] List fonts in a font atlas and display its texture
16073
void ImGui::ShowFontAtlas(ImFontAtlas* atlas)
16074
0
{
16075
0
    ImGuiContext& g = *GImGui;
16076
0
    ImGuiIO& io = g.IO;
16077
0
    ImGuiStyle& style = g.Style;
16078
16079
0
    BeginDisabled();
16080
0
    CheckboxFlags("io.BackendFlags: RendererHasTextures", &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
16081
0
    EndDisabled();
16082
0
    ShowFontSelector("Font");
16083
    //BeginDisabled((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0);
16084
0
    if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
16085
0
        style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
16086
0
    SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
16087
0
    SameLine(); MetricsHelpMarker("- This is scaling font only. General scaling will come later.");
16088
0
    DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);
16089
    //BeginDisabled(io.ConfigDpiScaleFonts);
16090
0
    DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f);
16091
    //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
16092
    //EndDisabled();
16093
0
    if ((io.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
16094
0
    {
16095
0
        BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
16096
0
        BulletText("For instructions, see:");
16097
0
        SameLine();
16098
0
        TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md");
16099
0
    }
16100
0
    BulletText("Load a nice font for better results!");
16101
0
    BulletText("Please submit feedback:");
16102
0
    SameLine(); TextLinkOpenURL("#8465", "https://github.com/ocornut/imgui/issues/8465");
16103
0
    BulletText("Read FAQ for more details:");
16104
0
    SameLine(); TextLinkOpenURL("dearimgui.com/faq", "https://www.dearimgui.com/faq/");
16105
    //EndDisabled();
16106
16107
0
    SeparatorText("Font List");
16108
16109
0
    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16110
0
    Checkbox("Show font preview", &cfg->ShowFontPreview);
16111
16112
    // Font loaders
16113
0
    if (TreeNode("Loader", "Loader: \'%s\'", atlas->FontLoaderName ? atlas->FontLoaderName : "NULL"))
16114
0
    {
16115
0
        const ImFontLoader* loader_current = atlas->FontLoader;
16116
0
        BeginDisabled(!atlas->RendererHasTextures);
16117
0
#ifdef IMGUI_ENABLE_STB_TRUETYPE
16118
0
        const ImFontLoader* loader_stbtruetype = ImFontAtlasGetFontLoaderForStbTruetype();
16119
0
        if (RadioButton("stb_truetype", loader_current == loader_stbtruetype))
16120
0
            atlas->SetFontLoader(loader_stbtruetype);
16121
#else
16122
        BeginDisabled();
16123
        RadioButton("stb_truetype", false);
16124
        SetItemTooltip("Requires #define IMGUI_ENABLE_STB_TRUETYPE");
16125
        EndDisabled();
16126
#endif
16127
0
        SameLine();
16128
#ifdef IMGUI_ENABLE_FREETYPE
16129
        const ImFontLoader* loader_freetype = ImGuiFreeType::GetFontLoader();
16130
        if (RadioButton("FreeType", loader_current == loader_freetype))
16131
            atlas->SetFontLoader(loader_freetype);
16132
        if (loader_current == loader_freetype)
16133
        {
16134
            unsigned int loader_flags = atlas->FontLoaderFlags;
16135
            Text("Shared FreeType Loader Flags:  0x%08X", loader_flags);
16136
            if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))
16137
            {
16138
                for (ImFont* font : atlas->Fonts)
16139
                    ImFontAtlasFontDestroyOutput(atlas, font);
16140
                atlas->FontLoaderFlags = loader_flags;
16141
                for (ImFont* font : atlas->Fonts)
16142
                    ImFontAtlasFontInitOutput(atlas, font);
16143
            }
16144
        }
16145
#else
16146
0
        BeginDisabled();
16147
0
        RadioButton("FreeType", false);
16148
0
        SetItemTooltip("Requires #define IMGUI_ENABLE_FREETYPE + imgui_freetype.cpp.");
16149
0
        EndDisabled();
16150
0
#endif
16151
0
        EndDisabled();
16152
0
        TreePop();
16153
0
    }
16154
16155
    // Font list
16156
0
    for (ImFont* font : atlas->Fonts)
16157
0
    {
16158
0
        PushID(font);
16159
0
        DebugNodeFont(font);
16160
0
        PopID();
16161
0
    }
16162
16163
0
    SeparatorText("Font Atlas");
16164
0
    if (Button("Compact"))
16165
0
        atlas->CompactCache();
16166
0
    SameLine();
16167
0
    if (Button("Grow"))
16168
0
        ImFontAtlasTextureGrow(atlas);
16169
0
    SameLine();
16170
0
    if (Button("Clear All"))
16171
0
        ImFontAtlasBuildClear(atlas);
16172
0
    SetItemTooltip("Destroy cache and custom rectangles.");
16173
16174
0
    for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
16175
0
    {
16176
0
        ImTextureData* tex = atlas->TexList[tex_n];
16177
0
        if (tex_n > 0)
16178
0
            SameLine();
16179
0
        Text("Tex: %dx%d", tex->Width, tex->Height);
16180
0
    }
16181
0
    const int packed_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsPackedSurface);
16182
0
    const int discarded_surface_sqrt = (int)sqrtf((float)atlas->Builder->RectsDiscardedSurface);
16183
0
    Text("Packed rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsPackedCount, atlas->Builder->RectsPackedSurface, packed_surface_sqrt, packed_surface_sqrt);
16184
0
    Text("incl. Discarded rects: %d, area: about %d px ~%dx%d px", atlas->Builder->RectsDiscardedCount, atlas->Builder->RectsDiscardedSurface, discarded_surface_sqrt, discarded_surface_sqrt);
16185
16186
0
    ImFontAtlasRectId highlight_r_id = ImFontAtlasRectId_Invalid;
16187
0
    if (TreeNode("Rects Index", "Rects Index (%d)", atlas->Builder->RectsPackedCount)) // <-- Use count of used rectangles
16188
0
    {
16189
0
        PushStyleVar(ImGuiStyleVar_ImageBorderSize, 1.0f);
16190
0
        if (BeginTable("##table", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY, ImVec2(0.0f, GetTextLineHeightWithSpacing() * 12)))
16191
0
        {
16192
0
            for (const ImFontAtlasRectEntry& entry : atlas->Builder->RectsIndex)
16193
0
                if (entry.IsUsed)
16194
0
                {
16195
0
                    ImFontAtlasRectId id = ImFontAtlasRectId_Make(atlas->Builder->RectsIndex.index_from_ptr(&entry), entry.Generation);
16196
0
                    ImFontAtlasRect r = {};
16197
0
                    atlas->GetCustomRect(id, &r);
16198
0
                    const char* buf;
16199
0
                    ImFormatStringToTempBuffer(&buf, NULL, "ID:%08X, used:%d, { w:%3d, h:%3d } { x:%4d, y:%4d }", id, entry.IsUsed, r.w, r.h, r.x, r.y);
16200
0
                    TableNextColumn();
16201
0
                    Selectable(buf);
16202
0
                    if (IsItemHovered())
16203
0
                        highlight_r_id = id;
16204
0
                    TableNextColumn();
16205
0
                    Image(atlas->TexRef, ImVec2(r.w, r.h), r.uv0, r.uv1);
16206
0
                }
16207
0
            EndTable();
16208
0
        }
16209
0
        PopStyleVar();
16210
0
        TreePop();
16211
0
    }
16212
16213
    // Texture list
16214
    // (ensure the last texture always use the same ID, so we can keep it open neatly)
16215
0
    ImFontAtlasRect highlight_r;
16216
0
    if (highlight_r_id != ImFontAtlasRectId_Invalid)
16217
0
        atlas->GetCustomRect(highlight_r_id, &highlight_r);
16218
0
    for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
16219
0
    {
16220
0
        if (tex_n == atlas->TexList.Size - 1)
16221
0
            SetNextItemOpen(true, ImGuiCond_Once);
16222
0
        DebugNodeTexture(atlas->TexList[tex_n], atlas->TexList.Size - 1 - tex_n, (highlight_r_id != ImFontAtlasRectId_Invalid) ? &highlight_r : NULL);
16223
0
    }
16224
0
}
16225
16226
void ImGui::DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect)
16227
0
{
16228
0
    ImGuiContext& g = *GImGui;
16229
0
    PushID(int_id);
16230
0
    if (TreeNode("", "Texture #%03d (%dx%d pixels)", tex->UniqueID, tex->Width, tex->Height))
16231
0
    {
16232
0
        ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16233
0
        Checkbox("Show used rect", &cfg->ShowTextureUsedRect);
16234
0
        PushStyleVar(ImGuiStyleVar_ImageBorderSize, ImMax(1.0f, g.Style.ImageBorderSize));
16235
0
        ImVec2 p = GetCursorScreenPos();
16236
0
        if (tex->WantDestroyNextFrame)
16237
0
            Dummy(ImVec2((float)tex->Width, (float)tex->Height));
16238
0
        else
16239
0
            ImageWithBg(tex->GetTexRef(), ImVec2((float)tex->Width, (float)tex->Height), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
16240
0
        if (cfg->ShowTextureUsedRect)
16241
0
            GetWindowDrawList()->AddRect(ImVec2(p.x + tex->UsedRect.x, p.y + tex->UsedRect.y), ImVec2(p.x + tex->UsedRect.x + tex->UsedRect.w, p.y + tex->UsedRect.y + tex->UsedRect.h), IM_COL32(255, 0, 255, 255));
16242
0
        if (highlight_rect != NULL)
16243
0
        {
16244
0
            ImRect r_outer(p.x, p.y, p.x + tex->Width, p.y + tex->Height);
16245
0
            ImRect r_inner(p.x + highlight_rect->x, p.y + highlight_rect->y, p.x + highlight_rect->x + highlight_rect->w, p.y + highlight_rect->y + highlight_rect->h);
16246
0
            RenderRectFilledWithHole(GetWindowDrawList(), r_outer, r_inner, IM_COL32(0, 0, 0, 100), 0.0f);
16247
0
            GetWindowDrawList()->AddRect(r_inner.Min - ImVec2(1, 1), r_inner.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255));
16248
0
        }
16249
0
        PopStyleVar();
16250
16251
0
        char texid_desc[30];
16252
0
        Text("Status = %s (%d), Format = %s (%d), UseColors = %d", ImTextureDataGetStatusName(tex->Status), tex->Status, ImTextureDataGetFormatName(tex->Format), tex->Format, tex->UseColors);
16253
0
        Text("TexID = %s, BackendUserData = %p", FormatTextureRefForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), tex->GetTexRef()), tex->BackendUserData);
16254
0
        TreePop();
16255
0
    }
16256
0
    PopID();
16257
0
}
16258
16259
void ImGui::ShowMetricsWindow(bool* p_open)
16260
0
{
16261
0
    ImGuiContext& g = *GImGui;
16262
0
    ImGuiIO& io = g.IO;
16263
0
    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16264
0
    if (cfg->ShowDebugLog)
16265
0
        ShowDebugLogWindow(&cfg->ShowDebugLog);
16266
0
    if (cfg->ShowIDStackTool)
16267
0
        ShowIDStackToolWindow(&cfg->ShowIDStackTool);
16268
16269
0
    if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1)
16270
0
    {
16271
0
        End();
16272
0
        return;
16273
0
    }
16274
16275
    // [DEBUG] Clear debug breaks hooks after exactly one cycle.
16276
0
    DebugBreakClearData();
16277
16278
    // Basic info
16279
0
    Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
16280
0
    if (g.ContextName[0] != 0)
16281
0
    {
16282
0
        SameLine();
16283
0
        Text("(Context Name: \"%s\")", g.ContextName);
16284
0
    }
16285
0
    Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
16286
0
    Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);
16287
0
    Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);
16288
    //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; }
16289
16290
0
    Separator();
16291
16292
    // Debugging enums
16293
0
    enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type
16294
0
    const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentIdeal", "ContentRegionRect" };
16295
0
    enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type
16296
0
    const char* trt_rects_names[TRT_Count] = { "OuterRect", "InnerRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsWorkRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentFrozen", "ColumnsContentUnfrozen" };
16297
0
    if (cfg->ShowWindowsRectsType < 0)
16298
0
        cfg->ShowWindowsRectsType = WRT_WorkRect;
16299
0
    if (cfg->ShowTablesRectsType < 0)
16300
0
        cfg->ShowTablesRectsType = TRT_WorkRect;
16301
16302
0
    struct Funcs
16303
0
    {
16304
0
        static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)
16305
0
        {
16306
0
            ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance
16307
0
            if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }
16308
0
            else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }
16309
0
            else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }
16310
0
            else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }
16311
0
            else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }
16312
0
            else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }
16313
0
            else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }
16314
0
            else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }
16315
0
            else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }
16316
0
            else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate
16317
0
            else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }
16318
0
            else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }
16319
0
            else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }
16320
0
            IM_ASSERT(0);
16321
0
            return ImRect();
16322
0
        }
16323
16324
0
        static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)
16325
0
        {
16326
0
            if (rect_type == WRT_OuterRect)                 { return window->Rect(); }
16327
0
            else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }
16328
0
            else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }
16329
0
            else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }
16330
0
            else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }
16331
0
            else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
16332
0
            else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
16333
0
            else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }
16334
0
            IM_ASSERT(0);
16335
0
            return ImRect();
16336
0
        }
16337
0
    };
16338
16339
#ifdef IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
16340
    TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS is enabled.\nMust disable after use! Otherwise Dear ImGui will run slower.\n");
16341
#endif
16342
16343
    // Tools
16344
0
    if (TreeNode("Tools"))
16345
0
    {
16346
        // Debug Break features
16347
        // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
16348
0
        SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
16349
0
        SameLine();
16350
0
        MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
16351
0
        if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
16352
0
            DebugStartItemPicker();
16353
0
        Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent);
16354
16355
0
        SeparatorText("Visualize");
16356
16357
0
        Checkbox("Show Debug Log", &cfg->ShowDebugLog);
16358
0
        SameLine();
16359
0
        MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code.");
16360
16361
0
        Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool);
16362
0
        SameLine();
16363
0
        MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code.");
16364
16365
0
        Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder);
16366
0
        Checkbox("Show windows rectangles", &cfg->ShowWindowsRects);
16367
0
        SameLine();
16368
0
        SetNextItemWidth(GetFontSize() * 12);
16369
0
        cfg->ShowWindowsRects |= Combo("##show_windows_rect_type", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);
16370
0
        if (cfg->ShowWindowsRects && g.NavWindow != NULL)
16371
0
        {
16372
0
            BulletText("'%s':", g.NavWindow->Name);
16373
0
            Indent();
16374
0
            for (int rect_n = 0; rect_n < WRT_Count; rect_n++)
16375
0
            {
16376
0
                ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);
16377
0
                Text("(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);
16378
0
            }
16379
0
            Unindent();
16380
0
        }
16381
16382
0
        Checkbox("Show tables rectangles", &cfg->ShowTablesRects);
16383
0
        SameLine();
16384
0
        SetNextItemWidth(GetFontSize() * 12);
16385
0
        cfg->ShowTablesRects |= Combo("##show_table_rects_type", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);
16386
0
        if (cfg->ShowTablesRects && g.NavWindow != NULL)
16387
0
        {
16388
0
            for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16389
0
            {
16390
0
                ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16391
0
                if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))
16392
0
                    continue;
16393
16394
0
                BulletText("Table 0x%08X (%d columns, in '%s')", table->ID, table->ColumnsCount, table->OuterWindow->Name);
16395
0
                if (IsItemHovered())
16396
0
                    GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16397
0
                Indent();
16398
0
                char buf[128];
16399
0
                for (int rect_n = 0; rect_n < TRT_Count; rect_n++)
16400
0
                {
16401
0
                    if (rect_n >= TRT_ColumnsRect)
16402
0
                    {
16403
0
                        if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)
16404
0
                            continue;
16405
0
                        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16406
0
                        {
16407
0
                            ImRect r = Funcs::GetTableRect(table, rect_n, column_n);
16408
0
                            ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);
16409
0
                            Selectable(buf);
16410
0
                            if (IsItemHovered())
16411
0
                                GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16412
0
                        }
16413
0
                    }
16414
0
                    else
16415
0
                    {
16416
0
                        ImRect r = Funcs::GetTableRect(table, rect_n, -1);
16417
0
                        ImFormatString(buf, IM_ARRAYSIZE(buf), "(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);
16418
0
                        Selectable(buf);
16419
0
                        if (IsItemHovered())
16420
0
                            GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);
16421
0
                    }
16422
0
                }
16423
0
                Unindent();
16424
0
            }
16425
0
        }
16426
0
        Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data
16427
16428
0
        SeparatorText("Validate");
16429
16430
0
        Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop);
16431
0
        SameLine();
16432
0
        MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
16433
16434
0
        Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer);
16435
0
        SameLine();
16436
0
        MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.");
16437
0
        if (cfg->ShowTextEncodingViewer)
16438
0
        {
16439
0
            static char buf[64] = "";
16440
0
            SetNextItemWidth(-FLT_MIN);
16441
0
            InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
16442
0
            if (buf[0] != 0)
16443
0
                DebugTextEncoding(buf);
16444
0
        }
16445
16446
0
        TreePop();
16447
0
    }
16448
16449
    // Windows
16450
0
    if (TreeNode("Windows", "Windows (%d)", g.Windows.Size))
16451
0
    {
16452
        //SetNextItemOpen(true, ImGuiCond_Once);
16453
0
        DebugNodeWindowsList(&g.Windows, "By display order");
16454
0
        DebugNodeWindowsList(&g.WindowsFocusOrder, "By focus order (root windows)");
16455
0
        if (TreeNode("By submission order (begin stack)"))
16456
0
        {
16457
            // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!
16458
0
            ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;
16459
0
            temp_buffer.resize(0);
16460
0
            for (ImGuiWindow* window : g.Windows)
16461
0
                if (window->LastFrameActive + 1 >= g.FrameCount)
16462
0
                    temp_buffer.push_back(window);
16463
0
            struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };
16464
0
            ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);
16465
0
            DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);
16466
0
            TreePop();
16467
0
        }
16468
16469
0
        TreePop();
16470
0
    }
16471
16472
    // DrawLists
16473
0
    int drawlist_count = 0;
16474
0
    for (ImGuiViewportP* viewport : g.Viewports)
16475
0
        drawlist_count += viewport->DrawDataP.CmdLists.Size;
16476
0
    if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count))
16477
0
    {
16478
0
        Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh);
16479
0
        Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes);
16480
0
        for (ImGuiViewportP* viewport : g.Viewports)
16481
0
            for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
16482
0
                DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
16483
0
        TreePop();
16484
0
    }
16485
16486
    // Viewports
16487
0
    if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size))
16488
0
    {
16489
0
        SetNextItemOpen(true, ImGuiCond_Once);
16490
0
        if (TreeNode("Windows Minimap"))
16491
0
        {
16492
0
            RenderViewportsThumbnails();
16493
0
            TreePop();
16494
0
        }
16495
0
        cfg->HighlightViewportID = 0;
16496
16497
0
        for (ImGuiViewportP* viewport : g.Viewports)
16498
0
            DebugNodeViewport(viewport);
16499
0
        TreePop();
16500
0
    }
16501
16502
    // Details for Fonts
16503
0
    for (ImFontAtlas* atlas : g.FontAtlases)
16504
0
        if (TreeNode((void*)atlas, "Fonts (%d), Textures (%d)", atlas->Fonts.Size, atlas->TexList.Size))
16505
0
        {
16506
0
            ShowFontAtlas(atlas);
16507
0
            TreePop();
16508
0
        }
16509
16510
    // Details for Popups
16511
0
    if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
16512
0
    {
16513
0
        for (const ImGuiPopupData& popup_data : g.OpenPopupStack)
16514
0
        {
16515
            // As it's difficult to interact with tree nodes while popups are open, we display everything inline.
16516
0
            ImGuiWindow* window = popup_data.Window;
16517
0
            BulletText("PopupID: %08x, Window: '%s' (%s%s), RestoreNavWindow '%s', ParentWindow '%s'",
16518
0
                popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "",
16519
0
                popup_data.RestoreNavWindow ? popup_data.RestoreNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL");
16520
0
        }
16521
0
        TreePop();
16522
0
    }
16523
16524
    // Details for TabBars
16525
0
    if (TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.GetAliveCount()))
16526
0
    {
16527
0
        for (int n = 0; n < g.TabBars.GetMapSize(); n++)
16528
0
            if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))
16529
0
            {
16530
0
                PushID(tab_bar);
16531
0
                DebugNodeTabBar(tab_bar, "TabBar");
16532
0
                PopID();
16533
0
            }
16534
0
        TreePop();
16535
0
    }
16536
16537
    // Details for Tables
16538
0
    if (TreeNode("Tables", "Tables (%d)", g.Tables.GetAliveCount()))
16539
0
    {
16540
0
        for (int n = 0; n < g.Tables.GetMapSize(); n++)
16541
0
            if (ImGuiTable* table = g.Tables.TryGetMapData(n))
16542
0
                DebugNodeTable(table);
16543
0
        TreePop();
16544
0
    }
16545
16546
    // Details for InputText
16547
0
    if (TreeNode("InputText"))
16548
0
    {
16549
0
        DebugNodeInputTextState(&g.InputTextState);
16550
0
        TreePop();
16551
0
    }
16552
16553
    // Details for TypingSelect
16554
0
    if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))
16555
0
    {
16556
0
        DebugNodeTypingSelectState(&g.TypingSelectState);
16557
0
        TreePop();
16558
0
    }
16559
16560
    // Details for MultiSelect
16561
0
    if (TreeNode("MultiSelect", "MultiSelect (%d)", g.MultiSelectStorage.GetAliveCount()))
16562
0
    {
16563
0
        ImGuiBoxSelectState* bs = &g.BoxSelectState;
16564
0
        BulletText("BoxSelect ID=0x%08X, Starting = %d, Active %d", bs->ID, bs->IsStarting, bs->IsActive);
16565
0
        for (int n = 0; n < g.MultiSelectStorage.GetMapSize(); n++)
16566
0
            if (ImGuiMultiSelectState* state = g.MultiSelectStorage.TryGetMapData(n))
16567
0
                DebugNodeMultiSelectState(state);
16568
0
        TreePop();
16569
0
    }
16570
16571
    // Details for Docking
16572
#ifdef IMGUI_HAS_DOCK
16573
    if (TreeNode("Docking"))
16574
    {
16575
        TreePop();
16576
    }
16577
#endif // #ifdef IMGUI_HAS_DOCK
16578
16579
    // Settings
16580
0
    if (TreeNode("Settings"))
16581
0
    {
16582
0
        if (SmallButton("Clear"))
16583
0
            ClearIniSettings();
16584
0
        SameLine();
16585
0
        if (SmallButton("Save to memory"))
16586
0
            SaveIniSettingsToMemory();
16587
0
        SameLine();
16588
0
        if (SmallButton("Save to disk"))
16589
0
            SaveIniSettingsToDisk(g.IO.IniFilename);
16590
0
        SameLine();
16591
0
        if (g.IO.IniFilename)
16592
0
            Text("\"%s\"", g.IO.IniFilename);
16593
0
        else
16594
0
            TextUnformatted("<NULL>");
16595
0
        Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
16596
0
        Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer);
16597
0
        if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size))
16598
0
        {
16599
0
            for (ImGuiSettingsHandler& handler : g.SettingsHandlers)
16600
0
                BulletText("\"%s\"", handler.TypeName);
16601
0
            TreePop();
16602
0
        }
16603
0
        if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size()))
16604
0
        {
16605
0
            for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))
16606
0
                DebugNodeWindowSettings(settings);
16607
0
            TreePop();
16608
0
        }
16609
16610
0
        if (TreeNode("SettingsTables", "Settings packed data: Tables: %d bytes", g.SettingsTables.size()))
16611
0
        {
16612
0
            for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
16613
0
                DebugNodeTableSettings(settings);
16614
0
            TreePop();
16615
0
        }
16616
16617
#ifdef IMGUI_HAS_DOCK
16618
#endif // #ifdef IMGUI_HAS_DOCK
16619
16620
0
        if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size()))
16621
0
        {
16622
0
            InputTextMultiline("##Ini", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);
16623
0
            TreePop();
16624
0
        }
16625
0
        TreePop();
16626
0
    }
16627
16628
    // Settings
16629
0
    if (TreeNode("Memory allocations"))
16630
0
    {
16631
0
        ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;
16632
0
        Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount);
16633
0
        if (SmallButton("GC now")) { g.GcCompactAll = true; }
16634
0
        Text("Recent frames with allocations:");
16635
0
        int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);
16636
0
        for (int n = buf_size - 1; n >= 0; n--)
16637
0
        {
16638
0
            ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];
16639
0
            BulletText("Frame %06d: %+3d ( %2d alloc, %2d free )", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount);
16640
0
            if (n == 0)
16641
0
            {
16642
0
                SameLine();
16643
0
                Text("<- %d frames ago", g.FrameCount - entry->FrameCount);
16644
0
            }
16645
0
        }
16646
0
        TreePop();
16647
0
    }
16648
16649
0
    if (TreeNode("Inputs"))
16650
0
    {
16651
0
        Text("KEYBOARD/GAMEPAD/MOUSE KEYS");
16652
0
        {
16653
            // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
16654
0
            Indent();
16655
0
            Text("Keys down:");         for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); }
16656
0
            Text("Keys pressed:");      for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
16657
0
            Text("Keys released:");     for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (!IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); }
16658
0
            Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
16659
0
            Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
16660
0
            DebugRenderKeyboardPreview(GetWindowDrawList());
16661
0
            Unindent();
16662
0
        }
16663
16664
0
        Text("MOUSE STATE");
16665
0
        {
16666
0
            Indent();
16667
0
            if (IsMousePosValid())
16668
0
                Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
16669
0
            else
16670
0
                Text("Mouse pos: <INVALID>");
16671
0
            Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
16672
0
            int count = IM_ARRAYSIZE(io.MouseDown);
16673
0
            Text("Mouse down:");     for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
16674
0
            Text("Mouse clicked:");  for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
16675
0
            Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
16676
0
            Text("Mouse wheel: %.1f", io.MouseWheel);
16677
0
            Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
16678
0
            Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
16679
0
            Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
16680
0
            Unindent();
16681
0
        }
16682
16683
0
        Text("MOUSE WHEELING");
16684
0
        {
16685
0
            Indent();
16686
0
            Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL");
16687
0
            Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer);
16688
0
            Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : "<none>");
16689
0
            Unindent();
16690
0
        }
16691
16692
0
        Text("KEY OWNERS");
16693
0
        {
16694
0
            Indent();
16695
0
            if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
16696
0
                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
16697
0
                {
16698
0
                    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);
16699
0
                    if (owner_data->OwnerCurr == ImGuiKeyOwner_NoOwner)
16700
0
                        continue;
16701
0
                    Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr,
16702
0
                        owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : "");
16703
0
                    DebugLocateItemOnHover(owner_data->OwnerCurr);
16704
0
                }
16705
0
            EndChild();
16706
0
            Unindent();
16707
0
        }
16708
0
        Text("SHORTCUT ROUTING");
16709
0
        SameLine();
16710
0
        MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches.");
16711
0
        {
16712
0
            Indent();
16713
0
            if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))
16714
0
                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))
16715
0
                {
16716
0
                    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;
16717
0
                    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )
16718
0
                    {
16719
0
                        ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];
16720
0
                        ImGuiKeyChord key_chord = key | routing_data->Mods;
16721
0
                        Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore);
16722
0
                        DebugLocateItemOnHover(routing_data->RoutingCurr);
16723
0
                        if (g.IO.ConfigDebugIsDebuggerPresent)
16724
0
                        {
16725
0
                            SameLine();
16726
0
                            if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
16727
0
                                g.DebugBreakInShortcutRouting = key_chord;
16728
0
                        }
16729
0
                        idx = routing_data->NextEntryIndex;
16730
0
                    }
16731
0
                }
16732
0
            EndChild();
16733
0
            Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
16734
0
            Unindent();
16735
0
        }
16736
0
        TreePop();
16737
0
    }
16738
16739
0
    if (TreeNode("Internal state"))
16740
0
    {
16741
0
        Text("WINDOWING");
16742
0
        Indent();
16743
0
        Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
16744
0
        Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL");
16745
0
        Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
16746
0
        Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL");
16747
0
        Unindent();
16748
16749
0
        Text("ITEMS");
16750
0
        Indent();
16751
0
        Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));
16752
0
        DebugLocateItemOnHover(g.ActiveId);
16753
0
        Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
16754
0
        Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
16755
0
        Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
16756
0
        Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
16757
0
        Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
16758
0
        DebugLocateItemOnHover(g.DragDropPayload.SourceId);
16759
0
        Unindent();
16760
16761
0
        Text("NAV,FOCUS");
16762
0
        Indent();
16763
0
        Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL");
16764
0
        Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer);
16765
0
        DebugLocateItemOnHover(g.NavId);
16766
0
        Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource));
16767
0
        Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);
16768
0
        Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible);
16769
0
        Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);
16770
0
        Text("NavActivateFlags: %04X", g.NavActivateFlags);
16771
0
        Text("NavCursorVisible: %d, NavHighlightItemUnderNav: %d", g.NavCursorVisible, g.NavHighlightItemUnderNav);
16772
0
        Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId);
16773
0
        Text("NavFocusRoute[] = ");
16774
0
        for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--)
16775
0
        {
16776
0
            const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n];
16777
0
            SameLine(0.0f, 0.0f);
16778
0
            Text("0x%08X/", focus_scope.ID);
16779
0
            SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name);
16780
0
        }
16781
0
        Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL");
16782
0
        Unindent();
16783
16784
0
        TreePop();
16785
0
    }
16786
16787
    // Overlay: Display windows Rectangles and Begin Order
16788
0
    if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)
16789
0
    {
16790
0
        for (ImGuiWindow* window : g.Windows)
16791
0
        {
16792
0
            if (!window->WasActive)
16793
0
                continue;
16794
0
            ImDrawList* draw_list = GetForegroundDrawList(window);
16795
0
            if (cfg->ShowWindowsRects)
16796
0
            {
16797
0
                ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);
16798
0
                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16799
0
            }
16800
0
            if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))
16801
0
            {
16802
0
                char buf[32];
16803
0
                ImFormatString(buf, IM_ARRAYSIZE(buf), "%d", window->BeginOrderWithinContext);
16804
0
                float font_size = GetFontSize();
16805
0
                draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));
16806
0
                draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);
16807
0
            }
16808
0
        }
16809
0
    }
16810
16811
    // Overlay: Display Tables Rectangles
16812
0
    if (cfg->ShowTablesRects)
16813
0
    {
16814
0
        for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)
16815
0
        {
16816
0
            ImGuiTable* table = g.Tables.TryGetMapData(table_n);
16817
0
            if (table == NULL || table->LastFrameActive < g.FrameCount - 1)
16818
0
                continue;
16819
0
            ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);
16820
0
            if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)
16821
0
            {
16822
0
                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
16823
0
                {
16824
0
                    ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);
16825
0
                    ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);
16826
0
                    float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;
16827
0
                    draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);
16828
0
                }
16829
0
            }
16830
0
            else
16831
0
            {
16832
0
                ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);
16833
0
                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));
16834
0
            }
16835
0
        }
16836
0
    }
16837
16838
#ifdef IMGUI_HAS_DOCK
16839
    // Overlay: Display Docking info
16840
    if (show_docking_nodes && g.IO.KeyCtrl)
16841
    {
16842
    }
16843
#endif // #ifdef IMGUI_HAS_DOCK
16844
16845
0
    End();
16846
0
}
16847
16848
void ImGui::DebugBreakClearData()
16849
0
{
16850
    // Those fields are scattered in their respective subsystem to stay in hot-data locations
16851
0
    ImGuiContext& g = *GImGui;
16852
0
    g.DebugBreakInWindow = 0;
16853
0
    g.DebugBreakInTable = 0;
16854
0
    g.DebugBreakInShortcutRouting = ImGuiKey_None;
16855
0
}
16856
16857
void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
16858
0
{
16859
0
    if (!BeginItemTooltip())
16860
0
        return;
16861
0
    Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
16862
0
    Separator();
16863
0
    TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
16864
0
    Separator();
16865
0
    TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
16866
0
    EndTooltip();
16867
0
}
16868
16869
// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
16870
// In order to reduce interferences with the contents we are trying to debug into.
16871
bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
16872
0
{
16873
0
    ImGuiWindow* window = GetCurrentWindow();
16874
0
    if (window->SkipItems)
16875
0
        return false;
16876
16877
0
    ImGuiContext& g = *GImGui;
16878
0
    const ImGuiID id = window->GetID(label);
16879
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
16880
0
    ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
16881
0
    ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
16882
16883
0
    const ImRect bb(pos, pos + size);
16884
0
    ItemSize(size, 0.0f);
16885
0
    if (!ItemAdd(bb, id))
16886
0
        return false;
16887
16888
    // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
16889
0
    bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
16890
0
    bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
16891
0
    DebugBreakButtonTooltip(false, description_of_location);
16892
16893
0
    ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
16894
0
    ImVec4 hsv;
16895
0
    ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
16896
0
    ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
16897
16898
0
    RenderNavCursor(bb, id);
16899
0
    RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
16900
0
    RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
16901
16902
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
16903
0
    return pressed;
16904
0
}
16905
16906
// [DEBUG] Display contents of Columns
16907
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
16908
0
{
16909
0
    if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags))
16910
0
        return;
16911
0
    BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);
16912
0
    for (ImGuiOldColumnData& column : columns->Columns)
16913
0
        BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));
16914
0
    TreePop();
16915
0
}
16916
16917
// [DEBUG] Display contents of ImDrawList
16918
void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)
16919
0
{
16920
0
    ImGuiContext& g = *GImGui;
16921
0
    IM_UNUSED(viewport); // Used in docking branch
16922
0
    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
16923
0
    int cmd_count = draw_list->CmdBuffer.Size;
16924
0
    if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)
16925
0
        cmd_count--;
16926
0
    bool node_open = TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);
16927
0
    if (draw_list == GetWindowDrawList())
16928
0
    {
16929
0
        SameLine();
16930
0
        TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered)
16931
0
        if (node_open)
16932
0
            TreePop();
16933
0
        return;
16934
0
    }
16935
16936
0
    ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list
16937
0
    if (window && IsItemHovered() && fg_draw_list)
16938
0
        fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
16939
0
    if (!node_open)
16940
0
        return;
16941
16942
0
    if (window && !window->WasActive)
16943
0
        TextDisabled("Warning: owning Window is inactive. This DrawList is not being rendered!");
16944
16945
0
    for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)
16946
0
    {
16947
0
        if (pcmd->UserCallback)
16948
0
        {
16949
0
            BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData);
16950
0
            continue;
16951
0
        }
16952
16953
0
        char texid_desc[30];
16954
0
        FormatTextureRefForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TexRef);
16955
0
        char buf[300];
16956
0
        ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)",
16957
0
            pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);
16958
0
        bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf);
16959
0
        if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)
16960
0
            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);
16961
0
        if (!pcmd_node_open)
16962
0
            continue;
16963
16964
        // Calculate approximate coverage area (touched pixel count)
16965
        // This will be in pixels squared as long there's no post-scaling happening to the renderer output.
16966
0
        const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;
16967
0
        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;
16968
0
        float total_area = 0.0f;
16969
0
        for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )
16970
0
        {
16971
0
            ImVec2 triangle[3];
16972
0
            for (int n = 0; n < 3; n++, idx_n++)
16973
0
                triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;
16974
0
            total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);
16975
0
        }
16976
16977
        // Display vertex information summary. Hover to get all triangles drawn in wire-frame
16978
0
        ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);
16979
0
        Selectable(buf);
16980
0
        if (IsItemHovered() && fg_draw_list)
16981
0
            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);
16982
16983
        // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
16984
0
        ImGuiListClipper clipper;
16985
0
        clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
16986
0
        while (clipper.Step())
16987
0
            for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
16988
0
            {
16989
0
                char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);
16990
0
                ImVec2 triangle[3];
16991
0
                for (int n = 0; n < 3; n++, idx_i++)
16992
0
                {
16993
0
                    const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];
16994
0
                    triangle[n] = v.pos;
16995
0
                    buf_p += ImFormatString(buf_p, buf_end - buf_p, "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n",
16996
0
                        (n == 0) ? "Vert:" : "     ", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);
16997
0
                }
16998
16999
0
                Selectable(buf, false);
17000
0
                if (fg_draw_list && IsItemHovered())
17001
0
                {
17002
0
                    ImDrawListFlags backup_flags = fg_draw_list->Flags;
17003
0
                    fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
17004
0
                    fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);
17005
0
                    fg_draw_list->Flags = backup_flags;
17006
0
                }
17007
0
            }
17008
0
        TreePop();
17009
0
    }
17010
0
    TreePop();
17011
0
}
17012
17013
// [DEBUG] Display mesh/aabb of a ImDrawCmd
17014
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)
17015
0
{
17016
0
    IM_ASSERT(show_mesh || show_aabb);
17017
17018
    // Draw wire-frame version of all triangles
17019
0
    ImRect clip_rect = draw_cmd->ClipRect;
17020
0
    ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
17021
0
    ImDrawListFlags backup_flags = out_draw_list->Flags;
17022
0
    out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.
17023
0
    for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )
17024
0
    {
17025
0
        ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list
17026
0
        ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;
17027
17028
0
        ImVec2 triangle[3];
17029
0
        for (int n = 0; n < 3; n++, idx_n++)
17030
0
            vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));
17031
0
        if (show_mesh)
17032
0
            out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles
17033
0
    }
17034
    // Draw bounding boxes
17035
0
    if (show_aabb)
17036
0
    {
17037
0
        out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU
17038
0
        out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles
17039
0
    }
17040
0
    out_draw_list->Flags = backup_flags;
17041
0
}
17042
17043
// [DEBUG] Compute mask of inputs with the same codepoint.
17044
static int CalcFontGlyphSrcOverlapMask(ImFontAtlas* atlas, ImFont* font, unsigned int codepoint)
17045
0
{
17046
0
    int mask = 0, count = 0;
17047
0
    for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17048
0
    {
17049
0
        ImFontConfig* src = font->Sources[src_n];
17050
0
        if (!(src->FontLoader ? src->FontLoader : atlas->FontLoader)->FontSrcContainsGlyph(atlas, src, (ImWchar)codepoint))
17051
0
            continue;
17052
0
        mask |= (1 << src_n);
17053
0
        count++;
17054
0
    }
17055
0
    return count > 1 ? mask : 0;
17056
0
}
17057
17058
// [DEBUG] Display details for a single font, called by ShowStyleEditor().
17059
void ImGui::DebugNodeFont(ImFont* font)
17060
0
{
17061
0
    ImGuiContext& g = *GImGui;
17062
0
    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;
17063
0
    ImFontAtlas* atlas = font->ContainerAtlas;
17064
0
    bool opened = TreeNode(font, "Font: \"%s\": %d sources(s)", font->GetDebugName(), font->Sources.Size);
17065
17066
    // Display preview text
17067
0
    if (!opened)
17068
0
        Indent();
17069
0
    Indent();
17070
0
    if (cfg->ShowFontPreview)
17071
0
    {
17072
0
        PushFont(font, 0.0f);
17073
0
        Text("The quick brown fox jumps over the lazy dog");
17074
0
        PopFont();
17075
0
    }
17076
0
    if (!opened)
17077
0
    {
17078
0
        Unindent();
17079
0
        Unindent();
17080
0
        return;
17081
0
    }
17082
0
    if (SmallButton("Set as default"))
17083
0
        GetIO().FontDefault = font;
17084
0
    SameLine();
17085
0
    BeginDisabled(atlas->Fonts.Size <= 1 || atlas->Locked);
17086
0
    if (SmallButton("Remove"))
17087
0
        atlas->RemoveFont(font);
17088
0
    EndDisabled();
17089
0
    SameLine();
17090
0
    if (SmallButton("Clear bakes"))
17091
0
        ImFontAtlasFontDiscardBakes(atlas, font, 0);
17092
0
    SameLine();
17093
0
    if (SmallButton("Clear unused"))
17094
0
        ImFontAtlasFontDiscardBakes(atlas, font, 2);
17095
17096
    // Display details
17097
0
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
17098
0
    SetNextItemWidth(GetFontSize() * 8);
17099
0
    DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f");
17100
    /*SameLine(); MetricsHelpMarker(
17101
        "Note that the default embedded font is NOT meant to be scaled.\n\n"
17102
        "Font are currently rendered into bitmaps at a given size at the time of building the atlas. "
17103
        "You may oversample them to get some flexibility with scaling. "
17104
        "You can also render at multiple sizes and select which one to use at runtime.\n\n"
17105
        "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)");*/
17106
0
#endif
17107
17108
0
    char c_str[5];
17109
0
    ImTextCharToUtf8(c_str, font->FallbackChar);
17110
0
    Text("Fallback character: '%s' (U+%04X)", c_str, font->FallbackChar);
17111
0
    ImTextCharToUtf8(c_str, font->EllipsisChar);
17112
0
    Text("Ellipsis character: '%s' (U+%04X)", c_str, font->EllipsisChar);
17113
17114
0
    for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17115
0
    {
17116
0
        ImFontConfig* src = font->Sources[src_n];
17117
0
        if (TreeNode(src, "Input %d: \'%s\' [%d], Oversample: %d,%d, PixelSnapH: %d, Offset: (%.1f,%.1f)",
17118
0
            src_n, src->Name, src->FontNo, src->OversampleH, src->OversampleV, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y))
17119
0
        {
17120
0
            const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
17121
0
            Text("Loader: '%s'", loader->Name ? loader->Name : "N/A");
17122
#ifdef IMGUI_ENABLE_FREETYPE
17123
            if (loader->Name != NULL && strcmp(loader->Name, "FreeType") == 0)
17124
            {
17125
                unsigned int loader_flags = src->FontLoaderFlags;
17126
                Text("FreeType Loader Flags: 0x%08X", loader_flags);
17127
                if (ImGuiFreeType::DebugEditFontLoaderFlags(&loader_flags))
17128
                {
17129
                    ImFontAtlasFontDestroyOutput(atlas, font);
17130
                    src->FontLoaderFlags = loader_flags;
17131
                    ImFontAtlasFontInitOutput(atlas, font);
17132
                }
17133
            }
17134
#endif
17135
0
            TreePop();
17136
0
        }
17137
0
    }
17138
0
    if (font->Sources.Size > 1 && TreeNode("Input Glyphs Overlap Detection Tool"))
17139
0
    {
17140
0
        TextWrapped("- First Input that contains the glyph is used.\n"
17141
0
            "- Use ImFontConfig::GlyphExcludeRanges[] to specify ranges to ignore glyph in given Input.\n- Prefer using a small number of ranges as the list is scanned every time a new glyph is loaded,\n  - e.g. GlyphExcludeRanges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };\n- This tool doesn't cache results and is slow, don't keep it open!");
17142
0
        if (BeginTable("table", 2))
17143
0
        {
17144
0
            for (unsigned int c = 0; c < 0x10000; c++)
17145
0
                if (int overlap_mask = CalcFontGlyphSrcOverlapMask(atlas, font, c))
17146
0
                {
17147
0
                    unsigned int c_end = c + 1;
17148
0
                    while (c_end < 0x10000 && CalcFontGlyphSrcOverlapMask(atlas, font, c_end) == overlap_mask)
17149
0
                        c_end++;
17150
0
                    if (TableNextColumn() && TreeNode((void*)(intptr_t)c, "U+%04X-U+%04X: %d codepoints in %d inputs", c, c_end - 1, c_end - c, ImCountSetBits(overlap_mask)))
17151
0
                    {
17152
0
                        char utf8_buf[5];
17153
0
                        for (unsigned int n = c; n < c_end; n++)
17154
0
                        {
17155
0
                            ImTextCharToUtf8(utf8_buf, n);
17156
0
                            BulletText("Codepoint U+%04X (%s)", n, utf8_buf);
17157
0
                        }
17158
0
                        TreePop();
17159
0
                    }
17160
0
                    TableNextColumn();
17161
0
                    for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17162
0
                        if (overlap_mask & (1 << src_n))
17163
0
                        {
17164
0
                            Text("%d ", src_n);
17165
0
                            SameLine();
17166
0
                        }
17167
0
                    c = c_end - 1;
17168
0
                }
17169
0
            EndTable();
17170
0
        }
17171
0
        TreePop();
17172
0
    }
17173
17174
    // Display all glyphs of the fonts in separate pages of 256 characters
17175
0
    for (int baked_n = 0; baked_n < atlas->Builder->BakedPool.Size; baked_n++)
17176
0
    {
17177
0
        ImFontBaked* baked = &atlas->Builder->BakedPool[baked_n];
17178
0
        if (baked->ContainerFont != font)
17179
0
            continue;
17180
0
        PushID(baked_n);
17181
0
        if (TreeNode("Glyphs", "Baked at { %.2fpx, d.%.2f }: %d glyphs%s", baked->Size, baked->RasterizerDensity, baked->Glyphs.Size, (baked->LastUsedFrame < atlas->Builder->FrameCount - 1) ? " *Unused*" : ""))
17182
0
        {
17183
0
            if (SmallButton("Load all"))
17184
0
                for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base++)
17185
0
                    baked->FindGlyph((ImWchar)base);
17186
17187
0
            const int surface_sqrt = (int)ImSqrt((float)baked->MetricsTotalSurface);
17188
0
            Text("Ascent: %f, Descent: %f, Ascent-Descent: %f", baked->Ascent, baked->Descent, baked->Ascent - baked->Descent);
17189
0
            Text("Texture Area: about %d px ~%dx%d px", baked->MetricsTotalSurface, surface_sqrt, surface_sqrt);
17190
0
            for (int src_n = 0; src_n < font->Sources.Size; src_n++)
17191
0
            {
17192
0
                ImFontConfig* src = font->Sources[src_n];
17193
0
                int oversample_h, oversample_v;
17194
0
                ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);
17195
0
                BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)",
17196
0
                    src_n, src->Name, src->OversampleH, oversample_h, src->OversampleV, oversample_v, src->PixelSnapH, src->GlyphOffset.x, src->GlyphOffset.y);
17197
0
            }
17198
17199
0
            DebugNodeFontGlyphesForSrcMask(font, baked, ~0);
17200
0
            TreePop();
17201
0
        }
17202
0
        PopID();
17203
0
    }
17204
0
    TreePop();
17205
0
    Unindent();
17206
0
}
17207
17208
void ImGui::DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask)
17209
0
{
17210
0
    ImDrawList* draw_list = GetWindowDrawList();
17211
0
    const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);
17212
0
    const float cell_size = baked->Size * 1;
17213
0
    const float cell_spacing = GetStyle().ItemSpacing.y;
17214
0
    for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)
17215
0
    {
17216
        // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)
17217
        // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT
17218
        // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)
17219
0
        if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191))
17220
0
        {
17221
0
            base += 8192 - 256;
17222
0
            continue;
17223
0
        }
17224
17225
0
        int count = 0;
17226
0
        for (unsigned int n = 0; n < 256; n++)
17227
0
            if (const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL)
17228
0
                if (src_mask & (1 << glyph->SourceIdx))
17229
0
                    count++;
17230
0
        if (count <= 0)
17231
0
            continue;
17232
0
        if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph"))
17233
0
            continue;
17234
17235
        // Draw a 16x16 grid of glyphs
17236
0
        ImVec2 base_pos = GetCursorScreenPos();
17237
0
        for (unsigned int n = 0; n < 256; n++)
17238
0
        {
17239
            // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions
17240
            // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.
17241
0
            ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));
17242
0
            ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);
17243
0
            const ImFontGlyph* glyph = baked->IsGlyphLoaded((ImWchar)(base + n)) ? baked->FindGlyph((ImWchar)(base + n)) : NULL;
17244
0
            draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));
17245
0
            if (!glyph || (src_mask & (1 << glyph->SourceIdx)) == 0)
17246
0
                continue;
17247
0
            font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));
17248
0
            if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())
17249
0
            {
17250
0
                DebugNodeFontGlyph(font, glyph);
17251
0
                EndTooltip();
17252
0
            }
17253
0
        }
17254
0
        Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));
17255
0
        TreePop();
17256
0
    }
17257
0
}
17258
17259
void ImGui::DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph)
17260
0
{
17261
0
    Text("Codepoint: U+%04X", glyph->Codepoint);
17262
0
    Separator();
17263
0
    Text("Visible: %d", glyph->Visible);
17264
0
    Text("AdvanceX: %.1f", glyph->AdvanceX);
17265
0
    Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);
17266
0
    Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1);
17267
0
    if (glyph->PackId >= 0)
17268
0
    {
17269
0
        ImTextureRect* r = ImFontAtlasPackGetRect(font->ContainerAtlas, glyph->PackId);
17270
0
        Text("PackId: 0x%X (%dx%d rect at %d,%d)", glyph->PackId, r->w, r->h, r->x, r->y);
17271
0
    }
17272
0
    Text("SourceIdx: %d", glyph->SourceIdx);
17273
0
}
17274
17275
// [DEBUG] Display contents of ImGuiStorage
17276
void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)
17277
0
{
17278
0
    if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes()))
17279
0
        return;
17280
0
    for (const ImGuiStoragePair& p : storage->Data)
17281
0
    {
17282
0
        BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.
17283
0
        DebugLocateItemOnHover(p.key);
17284
0
    }
17285
0
    TreePop();
17286
0
}
17287
17288
// [DEBUG] Display contents of ImGuiTabBar
17289
void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)
17290
0
{
17291
    // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.
17292
0
    char buf[256];
17293
0
    char* p = buf;
17294
0
    const char* buf_end = buf + IM_ARRAYSIZE(buf);
17295
0
    const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);
17296
0
    p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s  {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*");
17297
0
    for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)
17298
0
    {
17299
0
        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17300
0
        p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab));
17301
0
    }
17302
0
    p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } ");
17303
0
    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17304
0
    bool open = TreeNode(label, "%s", buf);
17305
0
    if (!is_active) { PopStyleColor(); }
17306
0
    if (is_active && IsItemHovered())
17307
0
    {
17308
0
        ImDrawList* draw_list = GetForegroundDrawList();
17309
0
        draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));
17310
0
        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
17311
0
        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));
17312
0
    }
17313
0
    if (open)
17314
0
    {
17315
0
        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
17316
0
        {
17317
0
            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
17318
0
            PushID(tab);
17319
0
            if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);
17320
0
            if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();
17321
0
            Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f",
17322
0
                tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);
17323
0
            PopID();
17324
0
        }
17325
0
        TreePop();
17326
0
    }
17327
0
}
17328
17329
void ImGui::DebugNodeViewport(ImGuiViewportP* viewport)
17330
0
{
17331
0
    ImGuiContext& g = *GImGui;
17332
0
    SetNextItemOpen(true, ImGuiCond_Once);
17333
0
    bool open = TreeNode("viewport0", "Viewport #%d", 0);
17334
0
    if (IsItemHovered())
17335
0
        g.DebugMetricsConfig.HighlightViewportID = viewport->ID;
17336
0
    if (open)
17337
0
    {
17338
0
        ImGuiWindowFlags flags = viewport->Flags;
17339
0
        BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f",
17340
0
            viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,
17341
0
            viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y);
17342
0
        BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags,
17343
0
            (flags & ImGuiViewportFlags_IsPlatformWindow)  ? " IsPlatformWindow"  : "",
17344
0
            (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "",
17345
0
            (flags & ImGuiViewportFlags_OwnedByApp)        ? " OwnedByApp"        : "");
17346
0
        for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)
17347
0
            DebugNodeDrawList(NULL, viewport, draw_list, "DrawList");
17348
0
        TreePop();
17349
0
    }
17350
0
}
17351
17352
void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
17353
0
{
17354
0
    if (window == NULL)
17355
0
    {
17356
0
        BulletText("%s: NULL", label);
17357
0
        return;
17358
0
    }
17359
17360
0
    ImGuiContext& g = *GImGui;
17361
0
    const bool is_active = window->WasActive;
17362
0
    ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;
17363
0
    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
17364
0
    const bool open = TreeNodeEx(label, tree_node_flags, "%s '%s'%s", label, window->Name, is_active ? "" : " *Inactive*");
17365
0
    if (!is_active) { PopStyleColor(); }
17366
0
    if (IsItemHovered() && is_active)
17367
0
        GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));
17368
0
    if (!open)
17369
0
        return;
17370
17371
0
    if (window->MemoryCompacted)
17372
0
        TextDisabled("Note: some memory buffers have been compacted/freed.");
17373
17374
0
    if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
17375
0
        g.DebugBreakInWindow = window->ID;
17376
17377
0
    ImGuiWindowFlags flags = window->Flags;
17378
0
    DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
17379
0
    BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
17380
0
    BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags,
17381
0
        (flags & ImGuiWindowFlags_ChildWindow)  ? "Child " : "",      (flags & ImGuiWindowFlags_Tooltip)     ? "Tooltip "   : "",  (flags & ImGuiWindowFlags_Popup) ? "Popup " : "",
17382
0
        (flags & ImGuiWindowFlags_Modal)        ? "Modal " : "",      (flags & ImGuiWindowFlags_ChildMenu)   ? "ChildMenu " : "",  (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "",
17383
0
        (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : "");
17384
0
    if (flags & ImGuiWindowFlags_ChildWindow)
17385
0
        BulletText("ChildFlags: 0x%08X (%s%s%s%s..)", window->ChildFlags,
17386
0
            (window->ChildFlags & ImGuiChildFlags_Borders) ? "Borders " : "",
17387
0
            (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "",
17388
0
            (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "",
17389
0
            (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : "");
17390
0
    BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : "");
17391
0
    BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);
17392
0
    BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);
17393
0
    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17394
0
    {
17395
0
        ImRect r = window->NavRectRel[layer];
17396
0
        if (r.Min.x >= r.Max.x && r.Min.y >= r.Max.y)
17397
0
            BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]);
17398
0
        else
17399
0
            BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);
17400
0
        DebugLocateItemOnHover(window->NavLastIds[layer]);
17401
0
    }
17402
0
    const ImVec2* pr = window->NavPreferredScoringPosRel;
17403
0
    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)
17404
0
        BulletText("NavPreferredScoringPosRel[%d] = (%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.
17405
0
    BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL");
17406
0
    if (window->RootWindow != window)               { DebugNodeWindow(window->RootWindow, "RootWindow"); }
17407
0
    if (window->ParentWindow != NULL)               { DebugNodeWindow(window->ParentWindow, "ParentWindow"); }
17408
0
    if (window->ParentWindowForFocusRoute != NULL)  { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); }
17409
0
    if (window->DC.ChildWindows.Size > 0)           { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); }
17410
0
    if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
17411
0
    {
17412
0
        for (ImGuiOldColumns& columns : window->ColumnsStorage)
17413
0
            DebugNodeColumns(&columns);
17414
0
        TreePop();
17415
0
    }
17416
0
    DebugNodeStorage(&window->StateStorage, "Storage");
17417
0
    TreePop();
17418
0
}
17419
17420
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)
17421
0
{
17422
0
    if (settings->WantDelete)
17423
0
        BeginDisabled();
17424
0
    Text("0x%08X \"%s\" Pos (%d,%d) Size (%d,%d) Collapsed=%d",
17425
0
        settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);
17426
0
    if (settings->WantDelete)
17427
0
        EndDisabled();
17428
0
}
17429
17430
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)
17431
0
{
17432
0
    if (!TreeNode(label, "%s (%d)", label, windows->Size))
17433
0
        return;
17434
0
    for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back
17435
0
    {
17436
0
        PushID((*windows)[i]);
17437
0
        DebugNodeWindow((*windows)[i], "Window");
17438
0
        PopID();
17439
0
    }
17440
0
    TreePop();
17441
0
}
17442
17443
// FIXME-OPT: This is technically suboptimal, but it is simpler this way.
17444
void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)
17445
0
{
17446
0
    for (int i = 0; i < windows_size; i++)
17447
0
    {
17448
0
        ImGuiWindow* window = windows[i];
17449
0
        if (window->ParentWindowInBeginStack != parent_in_begin_stack)
17450
0
            continue;
17451
0
        char buf[20];
17452
0
        ImFormatString(buf, IM_ARRAYSIZE(buf), "[%04d] Window", window->BeginOrderWithinContext);
17453
        //BulletText("[%04d] Window '%s'", window->BeginOrderWithinContext, window->Name);
17454
0
        DebugNodeWindow(window, buf);
17455
0
        TreePush(buf);
17456
0
        DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);
17457
0
        TreePop();
17458
0
    }
17459
0
}
17460
17461
//-----------------------------------------------------------------------------
17462
// [SECTION] DEBUG LOG WINDOW
17463
//-----------------------------------------------------------------------------
17464
17465
void ImGui::DebugLog(const char* fmt, ...)
17466
0
{
17467
0
    va_list args;
17468
0
    va_start(args, fmt);
17469
0
    DebugLogV(fmt, args);
17470
0
    va_end(args);
17471
0
}
17472
17473
void ImGui::DebugLogV(const char* fmt, va_list args)
17474
0
{
17475
0
    ImGuiContext& g = *GImGui;
17476
0
    const int old_size = g.DebugLogBuf.size();
17477
0
    if (g.ContextName[0] != 0)
17478
0
        g.DebugLogBuf.appendf("[%s] [%05d] ", g.ContextName, g.FrameCount);
17479
0
    else
17480
0
        g.DebugLogBuf.appendf("[%05d] ", g.FrameCount);
17481
0
    g.DebugLogBuf.appendfv(fmt, args);
17482
0
    g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());
17483
0
    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)
17484
0
        IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size);
17485
#ifdef IMGUI_ENABLE_TEST_ENGINE
17486
    // IMGUI_TEST_ENGINE_LOG() adds a trailing \n automatically
17487
    const int new_size = g.DebugLogBuf.size();
17488
    const bool trailing_carriage_return = (g.DebugLogBuf[new_size - 1] == '\n');
17489
    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)
17490
        IMGUI_TEST_ENGINE_LOG("%.*s", new_size - old_size - (trailing_carriage_return ? 1 : 0), g.DebugLogBuf.begin() + old_size);
17491
#endif
17492
0
}
17493
17494
// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout.
17495
static void SameLineOrWrap(const ImVec2& size)
17496
0
{
17497
0
    ImGuiContext& g = *GImGui;
17498
0
    ImGuiWindow* window = g.CurrentWindow;
17499
0
    ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y);
17500
0
    if (window->WorkRect.Contains(ImRect(pos, pos + size)))
17501
0
        ImGui::SameLine();
17502
0
}
17503
17504
static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags)
17505
0
{
17506
0
    ImGuiContext& g = *GImGui;
17507
0
    ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight());
17508
0
    SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout.
17509
17510
0
    bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0);
17511
0
    if (highlight_errors)
17512
0
        ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f));
17513
0
    if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0)
17514
0
    {
17515
0
        g.DebugLogAutoDisableFrames = 2;
17516
0
        g.DebugLogAutoDisableFlags |= flags;
17517
0
    }
17518
0
    if (highlight_errors)
17519
0
    {
17520
0
        ImGui::PopStyleColor();
17521
0
        ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors);
17522
0
    }
17523
0
    else
17524
0
    {
17525
0
        ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)");
17526
0
    }
17527
0
}
17528
17529
void ImGui::ShowDebugLogWindow(bool* p_open)
17530
0
{
17531
0
    ImGuiContext& g = *GImGui;
17532
0
    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)
17533
0
        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);
17534
0
    if (!Begin("Dear ImGui Debug Log", p_open) || GetCurrentWindow()->BeginCount > 1)
17535
0
    {
17536
0
        End();
17537
0
        return;
17538
0
    }
17539
17540
0
    ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting;
17541
0
    CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags);
17542
0
    SetItemTooltip("(except InputRouting which is spammy)");
17543
17544
0
    ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError);
17545
0
    ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId);
17546
0
    ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper);
17547
0
    ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus);
17548
0
    ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO);
17549
0
    ShowDebugLogFlag("Font", ImGuiDebugLogFlags_EventFont);
17550
0
    ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav);
17551
0
    ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup);
17552
0
    ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection);
17553
0
    ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting);
17554
17555
0
    if (SmallButton("Clear"))
17556
0
    {
17557
0
        g.DebugLogBuf.clear();
17558
0
        g.DebugLogIndex.clear();
17559
0
        g.DebugLogSkippedErrors = 0;
17560
0
    }
17561
0
    SameLine();
17562
0
    if (SmallButton("Copy"))
17563
0
        SetClipboardText(g.DebugLogBuf.c_str());
17564
0
    SameLine();
17565
0
    if (SmallButton("Configure Outputs.."))
17566
0
        OpenPopup("Outputs");
17567
0
    if (BeginPopup("Outputs"))
17568
0
    {
17569
0
        CheckboxFlags("OutputToTTY", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTTY);
17570
0
#ifndef IMGUI_ENABLE_TEST_ENGINE
17571
0
        BeginDisabled();
17572
0
#endif
17573
0
        CheckboxFlags("OutputToTestEngine", &g.DebugLogFlags, ImGuiDebugLogFlags_OutputToTestEngine);
17574
0
#ifndef IMGUI_ENABLE_TEST_ENGINE
17575
0
        EndDisabled();
17576
0
#endif
17577
0
        EndPopup();
17578
0
    }
17579
17580
0
    BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
17581
17582
0
    const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags;
17583
0
    g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;
17584
17585
0
    ImGuiListClipper clipper;
17586
0
    clipper.Begin(g.DebugLogIndex.size());
17587
0
    while (clipper.Step())
17588
0
        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
17589
0
            DebugTextUnformattedWithLocateItem(g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no), g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no));
17590
0
    g.DebugLogFlags = backup_log_flags;
17591
0
    if (GetScrollY() >= GetScrollMaxY())
17592
0
        SetScrollHereY(1.0f);
17593
0
    EndChild();
17594
17595
0
    End();
17596
0
}
17597
17598
// Display line, search for 0xXXXXXXXX identifiers and call DebugLocateItemOnHover() when hovered.
17599
void ImGui::DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end)
17600
0
{
17601
0
    TextUnformatted(line_begin, line_end);
17602
0
    if (!IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
17603
0
        return;
17604
0
    ImGuiContext& g = *GImGui;
17605
0
    ImRect text_rect = g.LastItemData.Rect;
17606
0
    for (const char* p = line_begin; p <= line_end - 10; p++)
17607
0
    {
17608
0
        ImGuiID id = 0;
17609
0
        if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1 || ImCharIsXdigitA(p[10]))
17610
0
            continue;
17611
0
        ImVec2 p0 = CalcTextSize(line_begin, p);
17612
0
        ImVec2 p1 = CalcTextSize(p, p + 10);
17613
0
        g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));
17614
0
        if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))
17615
0
            DebugLocateItemOnHover(id);
17616
0
        p += 10;
17617
0
    }
17618
0
}
17619
17620
//-----------------------------------------------------------------------------
17621
// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)
17622
//-----------------------------------------------------------------------------
17623
17624
// Draw a small cross at current CursorPos in current window's DrawList
17625
void ImGui::DebugDrawCursorPos(ImU32 col)
17626
0
{
17627
0
    ImGuiContext& g = *GImGui;
17628
0
    ImGuiWindow* window = g.CurrentWindow;
17629
0
    ImVec2 pos = window->DC.CursorPos;
17630
0
    window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);
17631
0
    window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);
17632
0
}
17633
17634
// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList
17635
void ImGui::DebugDrawLineExtents(ImU32 col)
17636
0
{
17637
0
    ImGuiContext& g = *GImGui;
17638
0
    ImGuiWindow* window = g.CurrentWindow;
17639
0
    float curr_x = window->DC.CursorPos.x;
17640
0
    float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);
17641
0
    float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);
17642
0
    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);
17643
0
    window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);
17644
0
    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);
17645
0
}
17646
17647
// Draw last item rect in ForegroundDrawList (so it is always visible)
17648
void ImGui::DebugDrawItemRect(ImU32 col)
17649
0
{
17650
0
    ImGuiContext& g = *GImGui;
17651
0
    ImGuiWindow* window = g.CurrentWindow;
17652
0
    GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);
17653
0
}
17654
17655
// [DEBUG] Locate item position/rectangle given an ID.
17656
static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255);  // Green
17657
17658
void ImGui::DebugLocateItem(ImGuiID target_id)
17659
0
{
17660
0
    ImGuiContext& g = *GImGui;
17661
0
    g.DebugLocateId = target_id;
17662
0
    g.DebugLocateFrames = 2;
17663
0
    g.DebugBreakInLocateId = false;
17664
0
}
17665
17666
// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow.
17667
void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
17668
0
{
17669
0
    if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))
17670
0
        return;
17671
0
    ImGuiContext& g = *GImGui;
17672
0
    DebugLocateItem(target_id);
17673
0
    GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);
17674
17675
    // Can't easily use a context menu here because it will mess with focus, active id etc.
17676
0
    if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
17677
0
    {
17678
0
        DebugBreakButtonTooltip(false, "in ItemAdd()");
17679
0
        if (IsKeyChordPressed(g.DebugBreakKeyChord))
17680
0
            g.DebugBreakInLocateId = true;
17681
0
    }
17682
0
}
17683
17684
void ImGui::DebugLocateItemResolveWithLastItem()
17685
0
{
17686
0
    ImGuiContext& g = *GImGui;
17687
17688
    // [DEBUG] Debug break requested by user
17689
0
    if (g.DebugBreakInLocateId)
17690
0
        IM_DEBUG_BREAK();
17691
17692
0
    ImGuiLastItemData item_data = g.LastItemData;
17693
0
    g.DebugLocateId = 0;
17694
0
    ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);
17695
0
    ImRect r = item_data.Rect;
17696
0
    r.Expand(3.0f);
17697
0
    ImVec2 p1 = g.IO.MousePos;
17698
0
    ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y);
17699
0
    draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);
17700
0
    draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);
17701
0
}
17702
17703
void ImGui::DebugStartItemPicker()
17704
0
{
17705
0
    ImGuiContext& g = *GImGui;
17706
0
    g.DebugItemPickerActive = true;
17707
0
}
17708
17709
// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.
17710
void ImGui::UpdateDebugToolItemPicker()
17711
80.5k
{
17712
80.5k
    ImGuiContext& g = *GImGui;
17713
80.5k
    g.DebugItemPickerBreakId = 0;
17714
80.5k
    if (!g.DebugItemPickerActive)
17715
80.5k
        return;
17716
17717
0
    const ImGuiID hovered_id = g.HoveredIdPreviousFrame;
17718
0
    SetMouseCursor(ImGuiMouseCursor_Hand);
17719
0
    if (IsKeyPressed(ImGuiKey_Escape))
17720
0
        g.DebugItemPickerActive = false;
17721
0
    const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);
17722
0
    if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)
17723
0
    {
17724
0
        g.DebugItemPickerBreakId = hovered_id;
17725
0
        g.DebugItemPickerActive = false;
17726
0
    }
17727
0
    for (int mouse_button = 0; mouse_button < 3; mouse_button++)
17728
0
        if (change_mapping && IsMouseClicked(mouse_button))
17729
0
            g.DebugItemPickerMouseButton = (ImU8)mouse_button;
17730
0
    SetNextWindowBgAlpha(0.70f);
17731
0
    if (!BeginTooltip())
17732
0
        return;
17733
0
    Text("HoveredId: 0x%08X", hovered_id);
17734
0
    Text("Press ESC to abort picking.");
17735
0
    const char* mouse_button_names[] = { "Left", "Right", "Middle" };
17736
0
    if (change_mapping)
17737
0
        Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button.");
17738
0
    else
17739
0
        TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]);
17740
0
    EndTooltip();
17741
0
}
17742
17743
// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()
17744
void ImGui::UpdateDebugToolStackQueries()
17745
80.5k
{
17746
80.5k
    ImGuiContext& g = *GImGui;
17747
80.5k
    ImGuiIDStackTool* tool = &g.DebugIDStackTool;
17748
17749
    // Clear hook when id stack tool is not visible
17750
80.5k
    g.DebugHookIdInfoId = 0;
17751
80.5k
    tool->QueryHookActive = false;
17752
80.5k
    if (g.FrameCount != tool->LastActiveFrame + 1)
17753
80.5k
        return;
17754
17755
    // Update queries. The steps are: -1: query Stack, >= 0: query each stack item
17756
    // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time
17757
0
    const ImGuiID query_main_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;
17758
0
    if (tool->QueryMainId != query_main_id)
17759
0
    {
17760
0
        tool->QueryMainId = query_main_id;
17761
0
        tool->StackLevel = -1;
17762
0
        tool->Results.resize(0);
17763
0
        tool->ResultPathsBuf.resize(0);
17764
0
    }
17765
0
    if (query_main_id == 0)
17766
0
        return;
17767
17768
    // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)
17769
0
    int stack_level = tool->StackLevel;
17770
0
    if (stack_level >= 0 && stack_level < tool->Results.Size)
17771
0
        if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)
17772
0
            tool->StackLevel++;
17773
17774
    // Update hook
17775
0
    stack_level = tool->StackLevel;
17776
0
    if (stack_level == -1)
17777
0
    {
17778
0
        g.DebugHookIdInfoId = query_main_id;
17779
0
        tool->QueryHookActive = true;
17780
0
    }
17781
0
    else if (stack_level >= 0 && stack_level < tool->Results.Size)
17782
0
    {
17783
0
        g.DebugHookIdInfoId = tool->Results[stack_level].ID;
17784
0
        tool->Results[stack_level].QueryFrameCount++;
17785
0
        tool->QueryHookActive = true;
17786
0
    }
17787
0
}
17788
17789
// [DEBUG] ID Stack tool: hooks called by GetID() family functions
17790
void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)
17791
0
{
17792
0
    ImGuiContext& g = *GImGui;
17793
0
    ImGuiWindow* window = g.CurrentWindow;
17794
0
    ImGuiIDStackTool* tool = &g.DebugIDStackTool;
17795
0
    if (tool->QueryHookActive == false)
17796
0
    {
17797
0
        IM_ASSERT(id == 0);
17798
0
        return;
17799
0
    }
17800
17801
    // Step 0: stack query
17802
    // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.
17803
0
    if (tool->StackLevel == -1)
17804
0
    {
17805
0
        IM_ASSERT(tool->Results.Size == 0);
17806
0
        tool->StackLevel++;
17807
0
        tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());
17808
0
        for (int n = 0; n < window->IDStack.Size + 1; n++)
17809
0
            tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;
17810
0
        return;
17811
0
    }
17812
17813
    // Step 1+: query for individual level
17814
0
    IM_ASSERT(tool->StackLevel >= 0);
17815
0
    if (tool->StackLevel != window->IDStack.Size)
17816
0
        return;
17817
0
    ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];
17818
0
    IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);
17819
17820
0
    if (info->DescOffset == -1)
17821
0
    {
17822
0
        const char* result = NULL;
17823
0
        const char* result_end = NULL;
17824
0
        switch (data_type)
17825
0
        {
17826
0
        case ImGuiDataType_S32:
17827
0
            ImFormatStringToTempBuffer(&result, &result_end, "%d", (int)(intptr_t)data_id);
17828
0
            break;
17829
0
        case ImGuiDataType_String:
17830
0
            ImFormatStringToTempBuffer(&result, &result_end, "%.*s", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)ImStrlen((const char*)data_id), (const char*)data_id);
17831
0
            break;
17832
0
        case ImGuiDataType_Pointer:
17833
0
            ImFormatStringToTempBuffer(&result, &result_end, "(void*)0x%p", data_id);
17834
0
            break;
17835
0
        case ImGuiDataType_ID:
17836
            // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.
17837
0
            ImFormatStringToTempBuffer(&result, &result_end, "0x%08X [override]", id);
17838
0
            break;
17839
0
        default:
17840
0
            IM_ASSERT(0);
17841
0
        }
17842
0
        info->DescOffset = tool->ResultPathsBuf.size();
17843
0
        tool->ResultPathsBuf.append(result, result_end + 1); // Include zero terminator
17844
0
    }
17845
0
    info->QuerySuccess = true;
17846
0
    info->DataType = (ImS8)data_type;
17847
0
}
17848
17849
static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)
17850
0
{
17851
0
    ImGuiStackLevelInfo* info = &tool->Results[n];
17852
0
    ImGuiWindow* window = (info->DescOffset == -1 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;
17853
0
    if (window)                                                                 // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)
17854
0
        return ImFormatString(buf, buf_size, format_for_ui ? "\"%s\" [window]" : "%s", ImHashSkipUncontributingPrefix(window->Name));
17855
0
    if (info->QuerySuccess)                                                     // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button("") where they both have same id)
17856
0
        return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? "\"%s\"" : "%s", ImHashSkipUncontributingPrefix(&tool->ResultPathsBuf.Buf[info->DescOffset]));
17857
0
    if (tool->StackLevel < tool->Results.Size)                                  // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.
17858
0
        return (*buf = 0);
17859
#ifdef IMGUI_ENABLE_TEST_ENGINE
17860
    if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID))   // Source: ImGuiTestEngine's ItemInfo()
17861
        return ImFormatString(buf, buf_size, format_for_ui ? "??? \"%s\"" : "%s", ImHashSkipUncontributingPrefix(label));
17862
#endif
17863
0
    return ImFormatString(buf, buf_size, "???");
17864
0
}
17865
17866
// ID Stack Tool: Display UI
17867
void ImGui::ShowIDStackToolWindow(bool* p_open)
17868
0
{
17869
0
    ImGuiContext& g = *GImGui;
17870
0
    if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0)
17871
0
        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);
17872
0
    if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1)
17873
0
    {
17874
0
        End();
17875
0
        return;
17876
0
    }
17877
17878
    // Display hovered/active status
17879
0
    ImGuiIDStackTool* tool = &g.DebugIDStackTool;
17880
17881
    // Build and display path
17882
0
    tool->ResultTempBuf.resize(0);
17883
0
    for (int stack_n = 0; stack_n < tool->Results.Size; stack_n++)
17884
0
    {
17885
0
        char level_desc[256];
17886
0
        StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));
17887
0
        tool->ResultTempBuf.append(stack_n == 0 ? "//" : "/");
17888
0
        for (const char* p = level_desc; *p != 0; )
17889
0
        {
17890
0
            unsigned int c;
17891
0
            const char* p_next = p + ImTextCharFromUtf8(&c, p, NULL);
17892
0
            if (c == '/')
17893
0
                tool->ResultTempBuf.append("\\");
17894
0
            if (c < 256 || !tool->OptHexEncodeNonAsciiChars)
17895
0
                tool->ResultTempBuf.append(p, p_next);
17896
0
            else for (; p < p_next; p++)
17897
0
                tool->ResultTempBuf.appendf("\\x%02x", (unsigned char)*p);
17898
0
            p = p_next;
17899
0
        }
17900
0
    }
17901
0
    Text("0x%08X", tool->QueryMainId);
17902
0
    SameLine();
17903
0
    MetricsHelpMarker("Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\nEach level of the stack correspond to a PushID() call.\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\nRead FAQ entry about the ID stack for details.");
17904
17905
    // CTRL+C to copy path
17906
0
    const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;
17907
0
    PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f);
17908
0
    Checkbox("Hex-encode non-ASCII", &tool->OptHexEncodeNonAsciiChars);
17909
0
    SameLine();
17910
0
    Checkbox("Ctrl+C: copy path", &tool->OptCopyToClipboardOnCtrlC);
17911
0
    PopStyleVar();
17912
0
    SameLine();
17913
0
    TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*");
17914
0
    if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused))
17915
0
    {
17916
0
        tool->CopyToClipboardLastTime = (float)g.Time;
17917
0
        SetClipboardText(tool->ResultTempBuf.c_str());
17918
0
    }
17919
17920
0
    Text("- Path \"%s\"", tool->ResultTempBuf.c_str());
17921
#ifdef IMGUI_ENABLE_TEST_ENGINE
17922
    Text("- Label \"%s\"", tool->QueryMainId ? ImGuiTestEngine_FindItemDebugLabel(&g, tool->QueryMainId) : "");
17923
#endif
17924
17925
0
    Separator();
17926
17927
    // Display decorated stack
17928
0
    tool->LastActiveFrame = g.FrameCount;
17929
0
    if (tool->Results.Size > 0 && BeginTable("##table", 3, ImGuiTableFlags_Borders))
17930
0
    {
17931
0
        const float id_width = CalcTextSize("0xDDDDDDDD").x;
17932
0
        TableSetupColumn("Seed", ImGuiTableColumnFlags_WidthFixed, id_width);
17933
0
        TableSetupColumn("PushID", ImGuiTableColumnFlags_WidthStretch);
17934
0
        TableSetupColumn("Result", ImGuiTableColumnFlags_WidthFixed, id_width);
17935
0
        TableHeadersRow();
17936
0
        for (int n = 0; n < tool->Results.Size; n++)
17937
0
        {
17938
0
            ImGuiStackLevelInfo* info = &tool->Results[n];
17939
0
            TableNextColumn();
17940
0
            Text("0x%08X", (n > 0) ? tool->Results[n - 1].ID : 0);
17941
0
            TableNextColumn();
17942
0
            StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);
17943
0
            TextUnformatted(g.TempBuffer.Data);
17944
0
            TableNextColumn();
17945
0
            Text("0x%08X", info->ID);
17946
0
            if (n == tool->Results.Size - 1)
17947
0
                TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));
17948
0
        }
17949
0
        EndTable();
17950
0
    }
17951
0
    End();
17952
0
}
17953
17954
#else
17955
17956
void ImGui::ShowMetricsWindow(bool*) {}
17957
void ImGui::ShowFontAtlas(ImFontAtlas*) {}
17958
void ImGui::DebugNodeColumns(ImGuiOldColumns*) {}
17959
void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}
17960
void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}
17961
void ImGui::DebugNodeFont(ImFont*) {}
17962
void ImGui::DebugNodeFontGlyphesForSrcMask(ImFont*, ImFontBaked*, int) {}
17963
void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}
17964
void ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}
17965
void ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}
17966
void ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}
17967
void ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}
17968
void ImGui::DebugNodeViewport(ImGuiViewportP*) {}
17969
17970
void ImGui::ShowDebugLogWindow(bool*) {}
17971
void ImGui::ShowIDStackToolWindow(bool*) {}
17972
void ImGui::DebugStartItemPicker() {}
17973
void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}
17974
17975
#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
17976
17977
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
17978
// Demo helper function to select among loaded fonts.
17979
// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.
17980
void ImGui::ShowFontSelector(const char* label)
17981
0
{
17982
0
    ImGuiIO& io = GetIO();
17983
0
    ImFont* font_current = GetFont();
17984
0
    if (BeginCombo(label, font_current->GetDebugName()))
17985
0
    {
17986
0
        for (ImFont* font : io.Fonts->Fonts)
17987
0
        {
17988
0
            PushID((void*)font);
17989
0
            if (Selectable(font->GetDebugName(), font == font_current, ImGuiSelectableFlags_SelectOnNav))
17990
0
                io.FontDefault = font;
17991
0
            if (font == font_current)
17992
0
                SetItemDefaultFocus();
17993
0
            PopID();
17994
0
        }
17995
0
        EndCombo();
17996
0
    }
17997
0
    SameLine();
17998
0
    if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures)
17999
0
        MetricsHelpMarker(
18000
0
            "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
18001
0
            "- Read FAQ and docs/FONTS.md for more details.");
18002
0
    else
18003
0
        MetricsHelpMarker(
18004
0
            "- Load additional fonts with io.Fonts->AddFontXXX() functions.\n"
18005
0
            "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n"
18006
0
            "- Read FAQ and docs/FONTS.md for more details.\n"
18007
0
            "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().");
18008
0
}
18009
#endif // #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) || !defined(IMGUI_DISABLE_DEBUG_TOOLS)
18010
18011
//-----------------------------------------------------------------------------
18012
18013
// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.
18014
// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.
18015
#ifdef IMGUI_INCLUDE_IMGUI_USER_INL
18016
#include "imgui_user.inl"
18017
#endif
18018
18019
//-----------------------------------------------------------------------------
18020
18021
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui.h
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (headers)
3
4
// Help:
5
// - See links below.
6
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7
// - Read top of imgui.cpp for more details, links and comments.
8
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
9
10
// Resources:
11
// - FAQ ........................ https://dearimgui.com/faq (in repository as docs/FAQ.md)
12
// - Homepage ................... https://github.com/ocornut/imgui
13
// - Releases & changelog ....... https://github.com/ocornut/imgui/releases
14
// - Gallery .................... https://github.com/ocornut/imgui/issues?q=label%3Agallery (please post your screenshots/video there!)
15
// - Wiki ....................... https://github.com/ocornut/imgui/wiki (lots of good stuff there)
16
//   - Getting Started            https://github.com/ocornut/imgui/wiki/Getting-Started (how to integrate in an existing app by adding ~25 lines of code)
17
//   - Third-party Extensions     https://github.com/ocornut/imgui/wiki/Useful-Extensions (ImPlot & many more)
18
//   - Bindings/Backends          https://github.com/ocornut/imgui/wiki/Bindings (language bindings, backends for various tech/engines)
19
//   - Glossary                   https://github.com/ocornut/imgui/wiki/Glossary
20
//   - Debug Tools                https://github.com/ocornut/imgui/wiki/Debug-Tools
21
//   - Software using Dear ImGui  https://github.com/ocornut/imgui/wiki/Software-using-dear-imgui
22
// - Issues & support ........... https://github.com/ocornut/imgui/issues
23
// - Test Engine & Automation ... https://github.com/ocornut/imgui_test_engine (test suite, test engine to automate your apps)
24
25
// For first-time users having issues compiling/linking/running/loading fonts:
26
// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.
27
// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.
28
29
// Library Version
30
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
31
6
#define IMGUI_VERSION       "1.92.4"
32
0
#define IMGUI_VERSION_NUM   19240
33
#define IMGUI_HAS_TABLE             // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000
34
#define IMGUI_HAS_TEXTURES          // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198
35
36
/*
37
38
Index of this file:
39
// [SECTION] Header mess
40
// [SECTION] Forward declarations and basic types
41
// [SECTION] Texture identifiers (ImTextureID, ImTextureRef)
42
// [SECTION] Dear ImGui end-user API functions
43
// [SECTION] Flags & Enumerations
44
// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
45
// [SECTION] Helpers: Debug log, Memory allocations macros, ImVector<>
46
// [SECTION] ImGuiStyle
47
// [SECTION] ImGuiIO
48
// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
49
// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
50
// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage)
51
// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)
52
// [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData)
53
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFontBaked, ImFont)
54
// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)
55
// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformImeData)
56
// [SECTION] Obsolete functions and types
57
58
*/
59
60
#pragma once
61
62
// Configuration file with compile-time options
63
// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system)
64
#ifdef IMGUI_USER_CONFIG
65
#include IMGUI_USER_CONFIG
66
#endif
67
#include "imconfig.h"
68
69
#ifndef IMGUI_DISABLE
70
71
//-----------------------------------------------------------------------------
72
// [SECTION] Header mess
73
//-----------------------------------------------------------------------------
74
75
// Includes
76
#include <float.h>                  // FLT_MIN, FLT_MAX
77
#include <stdarg.h>                 // va_list, va_start, va_end
78
#include <stddef.h>                 // ptrdiff_t, NULL
79
#include <string.h>                 // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp
80
81
// Define attributes of all API symbols declarations (e.g. for DLL under Windows)
82
// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h)
83
// Using dear imgui via a shared library is not recommended: we don't guarantee backward nor forward ABI compatibility + this is a call-heavy library and function call overhead adds up.
84
#ifndef IMGUI_API
85
#define IMGUI_API
86
#endif
87
#ifndef IMGUI_IMPL_API
88
#define IMGUI_IMPL_API              IMGUI_API
89
#endif
90
91
// Helper Macros
92
#ifndef IM_ASSERT
93
#include <assert.h>
94
0
#define IM_ASSERT(_EXPR)            assert(_EXPR)                               // You can override the default assert handler by editing imconfig.h
95
#endif
96
2.51M
#define IM_ARRAYSIZE(_ARR)          ((int)(sizeof(_ARR) / sizeof(*(_ARR))))     // Size of a static C-style array. Don't use on pointers!
97
410k
#define IM_UNUSED(_VAR)             ((void)(_VAR))                              // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
98
99
// Check that version and structures layouts are matching between compiled imgui code and caller. Read comments above DebugCheckVersionAndDataLayout() for details.
100
3
#define IMGUI_CHECKVERSION()        ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
101
102
// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.
103
// (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different
104
//  location. e.g. #include <sal.h> + void myprintf(_Printf_format_string_ const char* format, ...),
105
//  and only works when using Code Analysis, rather than just normal compiling).
106
// (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis)
107
#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)
108
#define IM_FMTARGS(FMT)             __attribute__((format(gnu_printf, FMT, FMT+1)))
109
#define IM_FMTLIST(FMT)             __attribute__((format(gnu_printf, FMT, 0)))
110
#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__))
111
#define IM_FMTARGS(FMT)             __attribute__((format(printf, FMT, FMT+1)))
112
#define IM_FMTLIST(FMT)             __attribute__((format(printf, FMT, 0)))
113
#else
114
#define IM_FMTARGS(FMT)
115
#define IM_FMTLIST(FMT)
116
#endif
117
118
// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions)
119
#if defined(_MSC_VER) && !defined(__clang__)  && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID)
120
#define IM_MSVC_RUNTIME_CHECKS_OFF      __pragma(runtime_checks("",off))     __pragma(check_stack(off)) __pragma(strict_gs_check(push,off))
121
#define IM_MSVC_RUNTIME_CHECKS_RESTORE  __pragma(runtime_checks("",restore)) __pragma(check_stack())    __pragma(strict_gs_check(pop))
122
#else
123
#define IM_MSVC_RUNTIME_CHECKS_OFF
124
#define IM_MSVC_RUNTIME_CHECKS_RESTORE
125
#endif
126
127
// Warnings
128
#ifdef _MSC_VER
129
#pragma warning (push)
130
#pragma warning (disable: 26495)    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
131
#endif
132
#if defined(__clang__)
133
#pragma clang diagnostic push
134
#if __has_warning("-Wunknown-warning-option")
135
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
136
#endif
137
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
138
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast
139
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe
140
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant
141
#pragma clang diagnostic ignored "-Wreserved-identifier"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
142
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
143
#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
144
#elif defined(__GNUC__)
145
#pragma GCC diagnostic push
146
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
147
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
148
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
149
#endif
150
151
//-----------------------------------------------------------------------------
152
// [SECTION] Forward declarations and basic types
153
//-----------------------------------------------------------------------------
154
155
// Scalar data types
156
typedef unsigned int        ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string)
157
typedef signed char         ImS8;   // 8-bit signed integer
158
typedef unsigned char       ImU8;   // 8-bit unsigned integer
159
typedef signed short        ImS16;  // 16-bit signed integer
160
typedef unsigned short      ImU16;  // 16-bit unsigned integer
161
typedef signed int          ImS32;  // 32-bit signed integer == int
162
typedef unsigned int        ImU32;  // 32-bit unsigned integer (often used to store packed colors)
163
typedef signed   long long  ImS64;  // 64-bit signed integer
164
typedef unsigned long long  ImU64;  // 64-bit unsigned integer
165
166
// Forward declarations: ImDrawList, ImFontAtlas layer
167
struct ImDrawChannel;               // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit()
168
struct ImDrawCmd;                   // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback)
169
struct ImDrawData;                  // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix.
170
struct ImDrawList;                  // A single draw command list (generally one per window, conceptually you may see this as a dynamic "mesh" builder)
171
struct ImDrawListSharedData;        // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself)
172
struct ImDrawListSplitter;          // Helper to split a draw list into different layers which can be drawn into out of order, then flattened back.
173
struct ImDrawVert;                  // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT)
174
struct ImFont;                      // Runtime data for a single font within a parent ImFontAtlas
175
struct ImFontAtlas;                 // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader
176
struct ImFontAtlasBuilder;          // Opaque storage for building a ImFontAtlas
177
struct ImFontAtlasRect;             // Output of ImFontAtlas::GetCustomRect() when using custom rectangles.
178
struct ImFontBaked;                 // Baked data for a ImFont at a given size.
179
struct ImFontConfig;                // Configuration data when adding a font or merging fonts
180
struct ImFontGlyph;                 // A single font glyph (code point + coordinates within in ImFontAtlas + offset)
181
struct ImFontGlyphRangesBuilder;    // Helper to build glyph ranges from text/string data
182
struct ImFontLoader;                // Opaque interface to a font loading backend (stb_truetype, FreeType etc.).
183
struct ImTextureData;               // Specs and pixel storage for a texture used by Dear ImGui.
184
struct ImTextureRect;               // Coordinates of a rectangle within a texture.
185
struct ImColor;                     // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using)
186
187
// Forward declarations: ImGui layer
188
struct ImGuiContext;                // Dear ImGui context (opaque structure, unless including imgui_internal.h)
189
struct ImGuiIO;                     // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO)
190
struct ImGuiInputTextCallbackData;  // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use)
191
struct ImGuiKeyData;                // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions.
192
struct ImGuiListClipper;            // Helper to manually clip large list of items
193
struct ImGuiMultiSelectIO;          // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block
194
struct ImGuiOnceUponAFrame;         // Helper for running a block of code not more than once a frame
195
struct ImGuiPayload;                // User data payload for drag and drop operations
196
struct ImGuiPlatformIO;             // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME hooks). Extends ImGuiIO. In docking branch, this gets extended to support multi-viewports.
197
struct ImGuiPlatformImeData;        // Platform IME data for io.PlatformSetImeDataFn() function.
198
struct ImGuiSelectionBasicStorage;  // Optional helper to store multi-selection state + apply multi-selection requests.
199
struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage.
200
struct ImGuiSelectionRequest;       // A selection request (stored in ImGuiMultiSelectIO)
201
struct ImGuiSizeCallbackData;       // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)
202
struct ImGuiStorage;                // Helper for key->value storage (container sorted by key)
203
struct ImGuiStoragePair;            // Helper for key->value storage (pair)
204
struct ImGuiStyle;                  // Runtime data for styling/colors
205
struct ImGuiTableSortSpecs;         // Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
206
struct ImGuiTableColumnSortSpecs;   // Sorting specification for one column of a table
207
struct ImGuiTextBuffer;             // Helper to hold and append into a text buffer (~string builder)
208
struct ImGuiTextFilter;             // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]")
209
struct ImGuiViewport;               // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor
210
211
// Enumerations
212
// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration)
213
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
214
//   - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
215
//   - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
216
//   - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
217
enum ImGuiDir : int;                // -> enum ImGuiDir              // Enum: A cardinal direction (Left, Right, Up, Down)
218
enum ImGuiKey : int;                // -> enum ImGuiKey              // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)
219
enum ImGuiMouseSource : int;        // -> enum ImGuiMouseSource      // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen)
220
enum ImGuiSortDirection : ImU8;     // -> enum ImGuiSortDirection    // Enum: A sorting direction (ascending or descending)
221
typedef int ImGuiCol;               // -> enum ImGuiCol_             // Enum: A color identifier for styling
222
typedef int ImGuiCond;              // -> enum ImGuiCond_            // Enum: A condition for many Set*() functions
223
typedef int ImGuiDataType;          // -> enum ImGuiDataType_        // Enum: A primary data type
224
typedef int ImGuiMouseButton;       // -> enum ImGuiMouseButton_     // Enum: A mouse button identifier (0=left, 1=right, 2=middle)
225
typedef int ImGuiMouseCursor;       // -> enum ImGuiMouseCursor_     // Enum: A mouse cursor shape
226
typedef int ImGuiStyleVar;          // -> enum ImGuiStyleVar_        // Enum: A variable identifier for styling
227
typedef int ImGuiTableBgTarget;     // -> enum ImGuiTableBgTarget_   // Enum: A color target for TableSetBgColor()
228
229
// Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file)
230
// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!
231
//   - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
232
//   - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
233
//   - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
234
typedef int ImDrawFlags;            // -> enum ImDrawFlags_          // Flags: for ImDrawList functions
235
typedef int ImDrawListFlags;        // -> enum ImDrawListFlags_      // Flags: for ImDrawList instance
236
typedef int ImDrawTextFlags;        // -> enum ImDrawTextFlags_      // Internal, do not use!
237
typedef int ImFontFlags;            // -> enum ImFontFlags_          // Flags: for ImFont
238
typedef int ImFontAtlasFlags;       // -> enum ImFontAtlasFlags_     // Flags: for ImFontAtlas
239
typedef int ImGuiBackendFlags;      // -> enum ImGuiBackendFlags_    // Flags: for io.BackendFlags
240
typedef int ImGuiButtonFlags;       // -> enum ImGuiButtonFlags_     // Flags: for InvisibleButton()
241
typedef int ImGuiChildFlags;        // -> enum ImGuiChildFlags_      // Flags: for BeginChild()
242
typedef int ImGuiColorEditFlags;    // -> enum ImGuiColorEditFlags_  // Flags: for ColorEdit4(), ColorPicker4() etc.
243
typedef int ImGuiConfigFlags;       // -> enum ImGuiConfigFlags_     // Flags: for io.ConfigFlags
244
typedef int ImGuiComboFlags;        // -> enum ImGuiComboFlags_      // Flags: for BeginCombo()
245
typedef int ImGuiDragDropFlags;     // -> enum ImGuiDragDropFlags_   // Flags: for BeginDragDropSource(), AcceptDragDropPayload()
246
typedef int ImGuiFocusedFlags;      // -> enum ImGuiFocusedFlags_    // Flags: for IsWindowFocused()
247
typedef int ImGuiHoveredFlags;      // -> enum ImGuiHoveredFlags_    // Flags: for IsItemHovered(), IsWindowHovered() etc.
248
typedef int ImGuiInputFlags;        // -> enum ImGuiInputFlags_      // Flags: for Shortcut(), SetNextItemShortcut()
249
typedef int ImGuiInputTextFlags;    // -> enum ImGuiInputTextFlags_  // Flags: for InputText(), InputTextMultiline()
250
typedef int ImGuiItemFlags;         // -> enum ImGuiItemFlags_       // Flags: for PushItemFlag(), shared by all items
251
typedef int ImGuiKeyChord;          // -> ImGuiKey | ImGuiMod_XXX    // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.
252
typedef int ImGuiListClipperFlags;  // -> enum ImGuiListClipperFlags_// Flags: for ImGuiListClipper
253
typedef int ImGuiPopupFlags;        // -> enum ImGuiPopupFlags_      // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()
254
typedef int ImGuiMultiSelectFlags;  // -> enum ImGuiMultiSelectFlags_// Flags: for BeginMultiSelect()
255
typedef int ImGuiSelectableFlags;   // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()
256
typedef int ImGuiSliderFlags;       // -> enum ImGuiSliderFlags_     // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
257
typedef int ImGuiTabBarFlags;       // -> enum ImGuiTabBarFlags_     // Flags: for BeginTabBar()
258
typedef int ImGuiTabItemFlags;      // -> enum ImGuiTabItemFlags_    // Flags: for BeginTabItem()
259
typedef int ImGuiTableFlags;        // -> enum ImGuiTableFlags_      // Flags: For BeginTable()
260
typedef int ImGuiTableColumnFlags;  // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn()
261
typedef int ImGuiTableRowFlags;     // -> enum ImGuiTableRowFlags_   // Flags: For TableNextRow()
262
typedef int ImGuiTreeNodeFlags;     // -> enum ImGuiTreeNodeFlags_   // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader()
263
typedef int ImGuiViewportFlags;     // -> enum ImGuiViewportFlags_   // Flags: for ImGuiViewport
264
typedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: for Begin(), BeginChild()
265
266
// Character types
267
// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)
268
typedef unsigned int ImWchar32;     // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings.
269
typedef unsigned short ImWchar16;   // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.
270
#ifdef IMGUI_USE_WCHAR32            // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16]
271
typedef ImWchar32 ImWchar;
272
#else
273
typedef ImWchar16 ImWchar;
274
#endif
275
276
// Multi-Selection item index or identifier when using BeginMultiSelect()
277
// - Used by SetNextItemSelectionUserData() + and inside ImGuiMultiSelectIO structure.
278
// - Most users are likely to use this store an item INDEX but this may be used to store a POINTER/ID as well. Read comments near ImGuiMultiSelectIO for details.
279
typedef ImS64 ImGuiSelectionUserData;
280
281
// Callback and functions types
282
typedef int     (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data);    // Callback function for ImGui::InputText()
283
typedef void    (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);              // Callback function for ImGui::SetNextWindowSizeConstraints()
284
typedef void*   (*ImGuiMemAllocFunc)(size_t sz, void* user_data);               // Function signature for ImGui::SetAllocatorFunctions()
285
typedef void    (*ImGuiMemFreeFunc)(void* ptr, void* user_data);                // Function signature for ImGui::SetAllocatorFunctions()
286
287
// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]
288
// - This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type.
289
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
290
IM_MSVC_RUNTIME_CHECKS_OFF
291
struct ImVec2
292
{
293
    float                                   x, y;
294
1.29M
    constexpr ImVec2()                      : x(0.0f), y(0.0f) { }
295
19.9M
    constexpr ImVec2(float _x, float _y)    : x(_x), y(_y) { }
296
2.10M
    float& operator[] (size_t idx)          { IM_ASSERT(idx == 0 || idx == 1); return ((float*)(void*)(char*)this)[idx]; } // We very rarely use this [] operator, so the assert overhead is fine.
297
0
    float  operator[] (size_t idx) const    { IM_ASSERT(idx == 0 || idx == 1); return ((const float*)(const void*)(const char*)this)[idx]; }
298
#ifdef IM_VEC2_CLASS_EXTRA
299
    IM_VEC2_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2.
300
#endif
301
};
302
303
// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type]
304
struct ImVec4
305
{
306
    float                                                     x, y, z, w;
307
405k
    constexpr ImVec4()                                        : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { }
308
646k
    constexpr ImVec4(float _x, float _y, float _z, float _w)  : x(_x), y(_y), z(_z), w(_w) { }
309
#ifdef IM_VEC4_CLASS_EXTRA
310
    IM_VEC4_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.
311
#endif
312
};
313
IM_MSVC_RUNTIME_CHECKS_RESTORE
314
315
//-----------------------------------------------------------------------------
316
// [SECTION] Texture identifiers (ImTextureID, ImTextureRef)
317
//-----------------------------------------------------------------------------
318
319
// ImTextureID = backend specific, low-level identifier for a texture uploaded in GPU/graphics system.
320
// [Compile-time configurable type]
321
// - When a Rendered Backend creates a texture, it store its native identifier into a ImTextureID value.
322
//   (e.g. Used by DX11 backend to a `ID3D11ShaderResourceView*`; Used by OpenGL backends to store `GLuint`;
323
//         Used by SDLGPU backend to store a `SDL_GPUTextureSamplerBinding*`, etc.).
324
// - User may submit their own textures to e.g. ImGui::Image() function by passing this value.
325
// - During the rendering loop, the Renderer Backend retrieve the ImTextureID, which stored inside a
326
//   ImTextureRef, which is stored inside a ImDrawCmd.
327
// - Compile-time type configuration:
328
//   - To use something other than a 64-bit value: add '#define ImTextureID MyTextureType*' in your imconfig.h file.
329
//   - This can be whatever to you want it to be! read the FAQ entry about textures for details.
330
//   - You may decide to store a higher-level structure containing texture, sampler, shader etc. with various
331
//     constructors if you like. You will need to implement ==/!= operators.
332
// History:
333
// - In v1.91.4 (2024/10/08): the default type for ImTextureID was changed from 'void*' to 'ImU64'. This allowed backends requirig 64-bit worth of data to build on 32-bit architectures. Use intermediary intptr_t cast and read FAQ if you have casting warnings.
334
// - In v1.92.0 (2025/06/11): added ImTextureRef which carry either a ImTextureID either a pointer to internal texture atlas. All user facing functions taking ImTextureID changed to ImTextureRef
335
#ifndef ImTextureID
336
typedef ImU64 ImTextureID;      // Default: store up to 64-bits (any pointer or integer). A majority of backends are ok with that.
337
#endif
338
339
// Define this if you need 0 to be a valid ImTextureID for your backend.
340
#ifndef ImTextureID_Invalid
341
485k
#define ImTextureID_Invalid     ((ImTextureID)0)
342
#endif
343
344
// ImTextureRef = higher-level identifier for a texture. Store a ImTextureID _or_ a ImTextureData*.
345
// The identifier is valid even before the texture has been uploaded to the GPU/graphics system.
346
// This is what gets passed to functions such as `ImGui::Image()`, `ImDrawList::AddImage()`.
347
// This is what gets stored in draw commands (`ImDrawCmd`) to identify a texture during rendering.
348
// - When a texture is created by user code (e.g. custom images), we directly stores the low-level ImTextureID.
349
//   Because of this, when displaying your own texture you are likely to ever only manage ImTextureID values on your side.
350
// - When a texture is created by the backend, we stores a ImTextureData* which becomes an indirection
351
//   to extract the ImTextureID value during rendering, after texture upload has happened.
352
// - To create a ImTextureRef from a ImTextureData you can use ImTextureData::GetTexRef().
353
//   We intentionally do not provide an ImTextureRef constructor for this: we don't expect this
354
//   to be frequently useful to the end-user, and it would be erroneously called by many legacy code.
355
// - If you want to bind the current atlas when using custom rectangle, you can use io.Fonts->TexRef.
356
// - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g.
357
//      inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; }
358
// In 1.92 we changed most drawing functions using ImTextureID to use ImTextureRef.
359
// We intentionally do not provide an implicit ImTextureRef -> ImTextureID cast operator because it is technically lossy to convert ImTextureRef to ImTextureID before rendering.
360
IM_MSVC_RUNTIME_CHECKS_OFF
361
struct ImTextureRef
362
{
363
404k
    ImTextureRef()                          { _TexData = NULL; _TexID = ImTextureID_Invalid; }
364
0
    ImTextureRef(ImTextureID tex_id)        { _TexData = NULL; _TexID = tex_id; }
365
#if !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(ImTextureID)
366
0
    ImTextureRef(void* tex_id)              { _TexData = NULL; _TexID = (ImTextureID)(size_t)tex_id; }  // For legacy backends casting to ImTextureID
367
#endif
368
369
    inline ImTextureID  GetTexID() const;   // == (_TexData ? _TexData->TexID : _TexID) // Implemented below in the file.
370
371
    // Members (either are set, never both!)
372
    ImTextureData*      _TexData;           //      A texture, generally owned by a ImFontAtlas. Will convert to ImTextureID during render loop, after texture has been uploaded.
373
    ImTextureID         _TexID;             // _OR_ Low-level backend texture identifier, if already uploaded or created by user/app. Generally provided to e.g. ImGui::Image() calls.
374
};
375
IM_MSVC_RUNTIME_CHECKS_RESTORE
376
377
//-----------------------------------------------------------------------------
378
// [SECTION] Dear ImGui end-user API functions
379
// (Note that ImGui:: being a namespace, you can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!)
380
//-----------------------------------------------------------------------------
381
382
namespace ImGui
383
{
384
    // Context creation and access
385
    // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts.
386
    // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
387
    //   for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for details.
388
    IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL);
389
    IMGUI_API void          DestroyContext(ImGuiContext* ctx = NULL);   // NULL = destroy current context
390
    IMGUI_API ImGuiContext* GetCurrentContext();
391
    IMGUI_API void          SetCurrentContext(ImGuiContext* ctx);
392
393
    // Main
394
    IMGUI_API ImGuiIO&      GetIO();                                    // access the ImGuiIO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags)
395
    IMGUI_API ImGuiPlatformIO& GetPlatformIO();                         // access the ImGuiPlatformIO structure (mostly hooks/functions to connect to platform/renderer and OS Clipboard, IME etc.)
396
    IMGUI_API ImGuiStyle&   GetStyle();                                 // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame!
397
    IMGUI_API void          NewFrame();                                 // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame().
398
    IMGUI_API void          EndFrame();                                 // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all!
399
    IMGUI_API void          Render();                                   // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData().
400
    IMGUI_API ImDrawData*   GetDrawData();                              // valid after Render() and until the next call to NewFrame(). Call ImGui_ImplXXXX_RenderDrawData() function in your Renderer Backend to render.
401
402
    // Demo, Debug, Information
403
    IMGUI_API void          ShowDemoWindow(bool* p_open = NULL);        // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!
404
    IMGUI_API void          ShowMetricsWindow(bool* p_open = NULL);     // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.
405
    IMGUI_API void          ShowDebugLogWindow(bool* p_open = NULL);    // create Debug Log window. display a simplified log of important dear imgui events.
406
    IMGUI_API void          ShowIDStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.
407
    IMGUI_API void          ShowAboutWindow(bool* p_open = NULL);       // create About window. display Dear ImGui version, credits and build/system information.
408
    IMGUI_API void          ShowStyleEditor(ImGuiStyle* ref = NULL);    // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)
409
    IMGUI_API bool          ShowStyleSelector(const char* label);       // add style selector block (not a window), essentially a combo listing the default styles.
410
    IMGUI_API void          ShowFontSelector(const char* label);        // add font selector block (not a window), essentially a combo listing the loaded fonts.
411
    IMGUI_API void          ShowUserGuide();                            // add basic help/info block (not a window): how to manipulate ImGui as an end-user (mouse/keyboard controls).
412
    IMGUI_API const char*   GetVersion();                               // get the compiled version string e.g. "1.80 WIP" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp)
413
414
    // Styles
415
    IMGUI_API void          StyleColorsDark(ImGuiStyle* dst = NULL);    // new, recommended style (default)
416
    IMGUI_API void          StyleColorsLight(ImGuiStyle* dst = NULL);   // best used with borders and a custom, thicker font
417
    IMGUI_API void          StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style
418
419
    // Windows
420
    // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack.
421
    // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window,
422
    //   which clicking will set the boolean to false when clicked.
423
    // - You may append multiple times to the same window during the same frame by calling Begin()/End() pairs multiple times.
424
    //   Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin().
425
    // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting
426
    //   anything to the window. Always call a matching End() for each Begin() call, regardless of its return value!
427
    //   [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions
428
    //    such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding
429
    //    BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
430
    // - Note that the bottom of window stack always contains a window called "Debug".
431
    IMGUI_API bool          Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0);
432
    IMGUI_API void          End();
433
434
    // Child Windows
435
    // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.
436
    // - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false".
437
    //   This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Borders == true.
438
    //   Consider updating your old code:
439
    //      BeginChild("Name", size, false)   -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None);
440
    //      BeginChild("Name", size, true)    -> Begin("Name", size, ImGuiChildFlags_Borders);
441
    // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)):
442
    //     == 0.0f: use remaining parent window size for this axis.
443
    //      > 0.0f: use specified size for this axis.
444
    //      < 0.0f: right/bottom-align to specified distance from available content boundaries.
445
    // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents.
446
    //   Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended.
447
    // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting
448
    //   anything to the window. Always call a matching EndChild() for each BeginChild() call, regardless of its return value.
449
    //   [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions
450
    //    such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding
451
    //    BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]
452
    IMGUI_API bool          BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);
453
    IMGUI_API bool          BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);
454
    IMGUI_API void          EndChild();
455
456
    // Windows Utilities
457
    // - 'current window' = the window we are appending into while inside a Begin()/End() block. 'next window' = next window we will Begin() into.
458
    IMGUI_API bool          IsWindowAppearing();
459
    IMGUI_API bool          IsWindowCollapsed();
460
    IMGUI_API bool          IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options.
461
    IMGUI_API bool          IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details.
462
    IMGUI_API ImDrawList*   GetWindowDrawList();                        // get draw list associated to the current window, to append your own drawing primitives
463
    IMGUI_API ImVec2        GetWindowPos();                             // get current window position in screen space (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead)
464
    IMGUI_API ImVec2        GetWindowSize();                            // get current window size (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead)
465
    IMGUI_API float         GetWindowWidth();                           // get current window width (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().x.
466
    IMGUI_API float         GetWindowHeight();                          // get current window height (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().y.
467
468
    // Window manipulation
469
    // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin).
470
    IMGUI_API void          SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc.
471
    IMGUI_API void          SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0);                  // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin()
472
    IMGUI_API void          SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use 0.0f or FLT_MAX if you don't want limits. Use -1 for both min and max of same axis to preserve current size (which itself is a constraint). Use callback to apply non-trivial programmatic constraints.
473
    IMGUI_API void          SetNextWindowContentSize(const ImVec2& size);                               // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin()
474
    IMGUI_API void          SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0);                 // set next window collapsed state. call before Begin()
475
    IMGUI_API void          SetNextWindowFocus();                                                       // set next window to be focused / top-most. call before Begin()
476
    IMGUI_API void          SetNextWindowScroll(const ImVec2& scroll);                                  // set next window scrolling value (use < 0.0f to not affect a given axis).
477
    IMGUI_API void          SetNextWindowBgAlpha(float alpha);                                          // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground.
478
    IMGUI_API void          SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0);                        // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects.
479
    IMGUI_API void          SetWindowSize(const ImVec2& size, ImGuiCond cond = 0);                      // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects.
480
    IMGUI_API void          SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0);                     // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed().
481
    IMGUI_API void          SetWindowFocus();                                                           // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus().
482
    IMGUI_API void          SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0);      // set named window position.
483
    IMGUI_API void          SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0);    // set named window size. set axis to 0.0f to force an auto-fit on this axis.
484
    IMGUI_API void          SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0);   // set named window collapsed state
485
    IMGUI_API void          SetWindowFocus(const char* name);                                           // set named window to be focused / top-most. use NULL to remove focus.
486
487
    // Windows Scrolling
488
    // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin().
489
    // - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY().
490
    IMGUI_API float         GetScrollX();                                                   // get scrolling amount [0 .. GetScrollMaxX()]
491
    IMGUI_API float         GetScrollY();                                                   // get scrolling amount [0 .. GetScrollMaxY()]
492
    IMGUI_API void          SetScrollX(float scroll_x);                                     // set scrolling amount [0 .. GetScrollMaxX()]
493
    IMGUI_API void          SetScrollY(float scroll_y);                                     // set scrolling amount [0 .. GetScrollMaxY()]
494
    IMGUI_API float         GetScrollMaxX();                                                // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - DecorationsSize.x
495
    IMGUI_API float         GetScrollMaxY();                                                // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - DecorationsSize.y
496
    IMGUI_API void          SetScrollHereX(float center_x_ratio = 0.5f);                    // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead.
497
    IMGUI_API void          SetScrollHereY(float center_y_ratio = 0.5f);                    // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead.
498
    IMGUI_API void          SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f);  // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position.
499
    IMGUI_API void          SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f);  // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position.
500
501
    // Parameters stacks (font)
502
    //  - PushFont(font, 0.0f)                       // Change font and keep current size
503
    //  - PushFont(NULL, 20.0f)                      // Keep font and change current size
504
    //  - PushFont(font, 20.0f)                      // Change font and set size to 20.0f
505
    //  - PushFont(font, style.FontSizeBase * 2.0f)  // Change font and set size to be twice bigger than current size.
506
    //  - PushFont(font, font->LegacySize)           // Change font and set size to size passed to AddFontXXX() function. Same as pre-1.92 behavior.
507
    // *IMPORTANT* before 1.92, fonts had a single size. They can now be dynamically be adjusted.
508
    //  - In 1.92 we have REMOVED the single parameter version of PushFont() because it seems like the easiest way to provide an error-proof transition.
509
    //  - PushFont(font) before 1.92 = PushFont(font, font->LegacySize) after 1.92          // Use default font size as passed to AddFontXXX() function.
510
    // *IMPORTANT* global scale factors are applied over the provided size.
511
    //  - Global scale factors are: 'style.FontScaleMain', 'style.FontScaleDpi' and maybe more.
512
    // -  If you want to apply a factor to the _current_ font size:
513
    //  - CORRECT:   PushFont(NULL, style.FontSizeBase)         // use current unscaled size    == does nothing
514
    //  - CORRECT:   PushFont(NULL, style.FontSizeBase * 2.0f)  // use current unscaled size x2 == make text twice bigger
515
    //  - INCORRECT: PushFont(NULL, GetFontSize())              // INCORRECT! using size after global factors already applied == GLOBAL SCALING FACTORS WILL APPLY TWICE!
516
    //  - INCORRECT: PushFont(NULL, GetFontSize() * 2.0f)       // INCORRECT! using size after global factors already applied == GLOBAL SCALING FACTORS WILL APPLY TWICE!
517
    IMGUI_API void          PushFont(ImFont* font, float font_size_base_unscaled);          // Use NULL as a shortcut to keep current font. Use 0.0f to keep current size.
518
    IMGUI_API void          PopFont();
519
    IMGUI_API ImFont*       GetFont();                                                      // get current font
520
    IMGUI_API float         GetFontSize();                                                  // get current scaled font size (= height in pixels). AFTER global scale factors applied. *IMPORTANT* DO NOT PASS THIS VALUE TO PushFont()! Use ImGui::GetStyle().FontSizeBase to get value before global scale factors.
521
    IMGUI_API ImFontBaked*  GetFontBaked();                                                 // get current font bound at current size // == GetFont()->GetFontBaked(GetFontSize())
522
523
    // Parameters stacks (shared)
524
    IMGUI_API void          PushStyleColor(ImGuiCol idx, ImU32 col);                        // modify a style color. always use this if you modify the style after NewFrame().
525
    IMGUI_API void          PushStyleColor(ImGuiCol idx, const ImVec4& col);
526
    IMGUI_API void          PopStyleColor(int count = 1);
527
    IMGUI_API void          PushStyleVar(ImGuiStyleVar idx, float val);                     // modify a style float variable. always use this if you modify the style after NewFrame()!
528
    IMGUI_API void          PushStyleVar(ImGuiStyleVar idx, const ImVec2& val);             // modify a style ImVec2 variable. "
529
    IMGUI_API void          PushStyleVarX(ImGuiStyleVar idx, float val_x);                  // modify X component of a style ImVec2 variable. "
530
    IMGUI_API void          PushStyleVarY(ImGuiStyleVar idx, float val_y);                  // modify Y component of a style ImVec2 variable. "
531
    IMGUI_API void          PopStyleVar(int count = 1);
532
    IMGUI_API void          PushItemFlag(ImGuiItemFlags option, bool enabled);              // modify specified shared item flag, e.g. PushItemFlag(ImGuiItemFlags_NoTabStop, true)
533
    IMGUI_API void          PopItemFlag();
534
535
    // Parameters stacks (current window)
536
    IMGUI_API void          PushItemWidth(float item_width);                                // push width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side).
537
    IMGUI_API void          PopItemWidth();
538
    IMGUI_API void          SetNextItemWidth(float item_width);                             // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side)
539
    IMGUI_API float         CalcItemWidth();                                                // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions.
540
    IMGUI_API void          PushTextWrapPos(float wrap_local_pos_x = 0.0f);                 // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space
541
    IMGUI_API void          PopTextWrapPos();
542
543
    // Style read access
544
    // - Use the ShowStyleEditor() function to interactively see/edit the colors.
545
    IMGUI_API ImVec2        GetFontTexUvWhitePixel();                                       // get UV coordinate for a white pixel, useful to draw custom shapes via the ImDrawList API
546
    IMGUI_API ImU32         GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f);              // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList
547
    IMGUI_API ImU32         GetColorU32(const ImVec4& col);                                 // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
548
    IMGUI_API ImU32         GetColorU32(ImU32 col, float alpha_mul = 1.0f);                 // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList
549
    IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx);                                // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.
550
551
    // Layout cursor positioning
552
    // - By "cursor" we mean the current output position.
553
    // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down.
554
    // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget.
555
    // - YOU CAN DO 99% OF WHAT YOU NEED WITH ONLY GetCursorScreenPos() and GetContentRegionAvail().
556
    // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API:
557
    //    - Absolute coordinate:        GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward.
558
    //    - Window-local coordinates:   SameLine(offset), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), PushTextWrapPos()
559
    //    - Window-local coordinates:   GetContentRegionMax(), GetWindowContentRegionMin(), GetWindowContentRegionMax() --> all obsoleted. YOU DON'T NEED THEM.
560
    // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. Try not to use it.
561
    IMGUI_API ImVec2        GetCursorScreenPos();                                           // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND (prefer using this rather than GetCursorPos(), also more useful to work with ImDrawList API).
562
    IMGUI_API void          SetCursorScreenPos(const ImVec2& pos);                          // cursor position, absolute coordinates. THIS IS YOUR BEST FRIEND.
563
    IMGUI_API ImVec2        GetContentRegionAvail();                                        // available space from current position. THIS IS YOUR BEST FRIEND.
564
    IMGUI_API ImVec2        GetCursorPos();                                                 // [window-local] cursor position in window-local coordinates. This is not your best friend.
565
    IMGUI_API float         GetCursorPosX();                                                // [window-local] "
566
    IMGUI_API float         GetCursorPosY();                                                // [window-local] "
567
    IMGUI_API void          SetCursorPos(const ImVec2& local_pos);                          // [window-local] "
568
    IMGUI_API void          SetCursorPosX(float local_x);                                   // [window-local] "
569
    IMGUI_API void          SetCursorPosY(float local_y);                                   // [window-local] "
570
    IMGUI_API ImVec2        GetCursorStartPos();                                            // [window-local] initial cursor position, in window-local coordinates. Call GetCursorScreenPos() after Begin() to get the absolute coordinates version.
571
572
    // Other layout functions
573
    IMGUI_API void          Separator();                                                    // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator.
574
    IMGUI_API void          SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f);  // call between widgets or groups to layout them horizontally. X position given in window coordinates.
575
    IMGUI_API void          NewLine();                                                      // undo a SameLine() or force a new line when in a horizontal-layout context.
576
    IMGUI_API void          Spacing();                                                      // add vertical spacing.
577
    IMGUI_API void          Dummy(const ImVec2& size);                                      // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into.
578
    IMGUI_API void          Indent(float indent_w = 0.0f);                                  // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0
579
    IMGUI_API void          Unindent(float indent_w = 0.0f);                                // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0
580
    IMGUI_API void          BeginGroup();                                                   // lock horizontal starting position
581
    IMGUI_API void          EndGroup();                                                     // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)
582
    IMGUI_API void          AlignTextToFramePadding();                                      // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item)
583
    IMGUI_API float         GetTextLineHeight();                                            // ~ FontSize
584
    IMGUI_API float         GetTextLineHeightWithSpacing();                                 // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text)
585
    IMGUI_API float         GetFrameHeight();                                               // ~ FontSize + style.FramePadding.y * 2
586
    IMGUI_API float         GetFrameHeightWithSpacing();                                    // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets)
587
588
    // ID stack/scopes
589
    // Read the FAQ (docs/FAQ.md or http://dearimgui.com/faq) for more details about how ID are handled in dear imgui.
590
    // - Those questions are answered and impacted by understanding of the ID stack system:
591
    //   - "Q: Why is my widget not reacting when I click on it?"
592
    //   - "Q: How can I have widgets with an empty label?"
593
    //   - "Q: How can I have multiple widgets with the same label?"
594
    // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely
595
    //   want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them.
596
    // - You can also use the "Label##foobar" syntax within widget label to distinguish them from each others.
597
    // - In this header file we use the "label"/"name" terminology to denote a string that will be displayed + used as an ID,
598
    //   whereas "str_id" denote a string that is only used as an ID and not normally displayed.
599
    IMGUI_API void          PushID(const char* str_id);                                     // push string into the ID stack (will hash string).
600
    IMGUI_API void          PushID(const char* str_id_begin, const char* str_id_end);       // push string into the ID stack (will hash string).
601
    IMGUI_API void          PushID(const void* ptr_id);                                     // push pointer into the ID stack (will hash pointer).
602
    IMGUI_API void          PushID(int int_id);                                             // push integer into the ID stack (will hash integer).
603
    IMGUI_API void          PopID();                                                        // pop from the ID stack.
604
    IMGUI_API ImGuiID       GetID(const char* str_id);                                      // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself
605
    IMGUI_API ImGuiID       GetID(const char* str_id_begin, const char* str_id_end);
606
    IMGUI_API ImGuiID       GetID(const void* ptr_id);
607
    IMGUI_API ImGuiID       GetID(int int_id);
608
609
    // Widgets: Text
610
    IMGUI_API void          TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text.
611
    IMGUI_API void          Text(const char* fmt, ...)                                      IM_FMTARGS(1); // formatted text
612
    IMGUI_API void          TextV(const char* fmt, va_list args)                            IM_FMTLIST(1);
613
    IMGUI_API void          TextColored(const ImVec4& col, const char* fmt, ...)            IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();
614
    IMGUI_API void          TextColoredV(const ImVec4& col, const char* fmt, va_list args)  IM_FMTLIST(2);
615
    IMGUI_API void          TextDisabled(const char* fmt, ...)                              IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor();
616
    IMGUI_API void          TextDisabledV(const char* fmt, va_list args)                    IM_FMTLIST(1);
617
    IMGUI_API void          TextWrapped(const char* fmt, ...)                               IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize().
618
    IMGUI_API void          TextWrappedV(const char* fmt, va_list args)                     IM_FMTLIST(1);
619
    IMGUI_API void          LabelText(const char* label, const char* fmt, ...)              IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets
620
    IMGUI_API void          LabelTextV(const char* label, const char* fmt, va_list args)    IM_FMTLIST(2);
621
    IMGUI_API void          BulletText(const char* fmt, ...)                                IM_FMTARGS(1); // shortcut for Bullet()+Text()
622
    IMGUI_API void          BulletTextV(const char* fmt, va_list args)                      IM_FMTLIST(1);
623
    IMGUI_API void          SeparatorText(const char* label);                               // currently: formatted text with a horizontal line
624
625
    // Widgets: Main
626
    // - Most widgets return true when the value has been changed or when pressed/selected
627
    // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.
628
    IMGUI_API bool          Button(const char* label, const ImVec2& size = ImVec2(0, 0));   // button
629
    IMGUI_API bool          SmallButton(const char* label);                                 // button with (FramePadding.y == 0) to easily embed within text
630
    IMGUI_API bool          InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)
631
    IMGUI_API bool          ArrowButton(const char* str_id, ImGuiDir dir);                  // square button with an arrow shape
632
    IMGUI_API bool          Checkbox(const char* label, bool* v);
633
    IMGUI_API bool          CheckboxFlags(const char* label, int* flags, int flags_value);
634
    IMGUI_API bool          CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value);
635
    IMGUI_API bool          RadioButton(const char* label, bool active);                    // use with e.g. if (RadioButton("one", my_value==1)) { my_value = 1; }
636
    IMGUI_API bool          RadioButton(const char* label, int* v, int v_button);           // shortcut to handle the above pattern when value is an integer
637
    IMGUI_API void          ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL);
638
    IMGUI_API void          Bullet();                                                       // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses
639
    IMGUI_API bool          TextLink(const char* label);                                    // hyperlink text button, return true when clicked
640
    IMGUI_API bool          TextLinkOpenURL(const char* label, const char* url = NULL);     // hyperlink text button, automatically open file/url when clicked
641
642
    // Widgets: Images
643
    // - Read about ImTextureID/ImTextureRef  here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
644
    // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
645
    // - Image() pads adds style.ImageBorderSize on each side, ImageButton() adds style.FramePadding on each side.
646
    // - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified.
647
    // - An obsolete version of Image(), before 1.91.9 (March 2025), had a 'tint_col' parameter which is now supported by the ImageWithBg() function.
648
    IMGUI_API void          Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1));
649
    IMGUI_API void          ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));
650
    IMGUI_API bool          ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));
651
652
    // Widgets: Combo Box (Dropdown)
653
    // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items.
654
    // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created.
655
    IMGUI_API bool          BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0);
656
    IMGUI_API void          EndCombo(); // only call EndCombo() if BeginCombo() returns true!
657
    IMGUI_API bool          Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1);
658
    IMGUI_API bool          Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1);      // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0"
659
    IMGUI_API bool          Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1);
660
661
    // Widgets: Drag Sliders
662
    // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
663
    // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, note that a 'float v[X]' function argument is the same as 'float* v',
664
    //   the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x
665
    // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
666
    // - Format string may also be set to NULL or use the default format ("%f" or "%d").
667
    // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For keyboard/gamepad navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).
668
    // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.
669
    // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.
670
    // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
671
    // - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
672
    //   If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361
673
    IMGUI_API bool          DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);     // If v_min >= v_max we have no bound
674
    IMGUI_API bool          DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
675
    IMGUI_API bool          DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
676
    IMGUI_API bool          DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
677
    IMGUI_API bool          DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, ImGuiSliderFlags flags = 0);
678
    IMGUI_API bool          DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);  // If v_min >= v_max we have no bound
679
    IMGUI_API bool          DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
680
    IMGUI_API bool          DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
681
    IMGUI_API bool          DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", ImGuiSliderFlags flags = 0);
682
    IMGUI_API bool          DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL, ImGuiSliderFlags flags = 0);
683
    IMGUI_API bool          DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
684
    IMGUI_API bool          DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);
685
686
    // Widgets: Regular Sliders
687
    // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.
688
    // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc.
689
    // - Format string may also be set to NULL or use the default format ("%f" or "%d").
690
    // - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.
691
    //   If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361
692
    IMGUI_API bool          SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);     // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display.
693
    IMGUI_API bool          SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
694
    IMGUI_API bool          SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
695
    IMGUI_API bool          SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
696
    IMGUI_API bool          SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = "%.0f deg", ImGuiSliderFlags flags = 0);
697
    IMGUI_API bool          SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
698
    IMGUI_API bool          SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
699
    IMGUI_API bool          SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
700
    IMGUI_API bool          SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
701
    IMGUI_API bool          SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
702
    IMGUI_API bool          SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
703
    IMGUI_API bool          VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0);
704
    IMGUI_API bool          VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d", ImGuiSliderFlags flags = 0);
705
    IMGUI_API bool          VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);
706
707
    // Widgets: Input with Keyboard
708
    // - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
709
    // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc.
710
    IMGUI_API bool          InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
711
    IMGUI_API bool          InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
712
    IMGUI_API bool          InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
713
    IMGUI_API bool          InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
714
    IMGUI_API bool          InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
715
    IMGUI_API bool          InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
716
    IMGUI_API bool          InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags flags = 0);
717
    IMGUI_API bool          InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0);
718
    IMGUI_API bool          InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0);
719
    IMGUI_API bool          InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0);
720
    IMGUI_API bool          InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0);
721
    IMGUI_API bool          InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags flags = 0);
722
    IMGUI_API bool          InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0);
723
    IMGUI_API bool          InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0);
724
725
    // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.)
726
    // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible.
727
    // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x
728
    IMGUI_API bool          ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
729
    IMGUI_API bool          ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
730
    IMGUI_API bool          ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
731
    IMGUI_API bool          ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);
732
    IMGUI_API bool          ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.
733
    IMGUI_API void          SetColorEditOptions(ImGuiColorEditFlags flags);                     // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.
734
735
    // Widgets: Trees
736
    // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents.
737
    IMGUI_API bool          TreeNode(const char* label);
738
    IMGUI_API bool          TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2);   // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet().
739
    IMGUI_API bool          TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2);   // "
740
    IMGUI_API bool          TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2);
741
    IMGUI_API bool          TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2);
742
    IMGUI_API bool          TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0);
743
    IMGUI_API bool          TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);
744
    IMGUI_API bool          TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);
745
    IMGUI_API bool          TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);
746
    IMGUI_API bool          TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);
747
    IMGUI_API void          TreePush(const char* str_id);                                       // ~ Indent()+PushID(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired.
748
    IMGUI_API void          TreePush(const void* ptr_id);                                       // "
749
    IMGUI_API void          TreePop();                                                          // ~ Unindent()+PopID()
750
    IMGUI_API float         GetTreeNodeToLabelSpacing();                                        // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode
751
    IMGUI_API bool          CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0);  // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop().
752
    IMGUI_API bool          CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header.
753
    IMGUI_API void          SetNextItemOpen(bool is_open, ImGuiCond cond = 0);                  // set next TreeNode/CollapsingHeader open state.
754
    IMGUI_API void          SetNextItemStorageID(ImGuiID storage_id);                           // set id to use for open/close storage (default to same as item id).
755
756
    // Widgets: Selectables
757
    // - A selectable highlights when hovered, and can display another color when selected.
758
    // - Neighbors selectable extend their highlight bounds in order to leave no gap between them. This is so a series of selected Selectable appear contiguous.
759
    IMGUI_API bool          Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height
760
    IMGUI_API bool          Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0));      // "bool* p_selected" point to the selection state (read-write), as a convenient helper.
761
762
    // Multi-selection system for Selectable(), Checkbox(), TreeNode() functions [BETA]
763
    // - This enables standard multi-selection/range-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc.) in a way that also allow a clipper to be used.
764
    // - ImGuiSelectionUserData is often used to store your item index within the current view (but may store something else).
765
    // - Read comments near ImGuiMultiSelectIO for instructions/details and see 'Demo->Widgets->Selection State & Multi-Select' for demo.
766
    // - TreeNode() is technically supported but... using this correctly is more complicated. You need some sort of linear/random access to your tree,
767
    //   which is suited to advanced trees setups already implementing filters and clipper. We will work simplifying the current demo.
768
    // - 'selection_size' and 'items_count' parameters are optional and used by a few features. If they are costly for you to compute, you may avoid them.
769
    IMGUI_API ImGuiMultiSelectIO*   BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size = -1, int items_count = -1);
770
    IMGUI_API ImGuiMultiSelectIO*   EndMultiSelect();
771
    IMGUI_API void                  SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);
772
    IMGUI_API bool                  IsItemToggledSelection();                                   // Was the last item selection state toggled? Useful if you need the per-item information _before_ reaching EndMultiSelect(). We only returns toggle _event_ in order to handle clipping correctly.
773
774
    // Widgets: List Boxes
775
    // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
776
    // - If you don't need a label you can probably simply use BeginChild() with the ImGuiChildFlags_FrameStyle flag for the same result.
777
    // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items.
778
    // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analogous to how Combos are created.
779
    // - Choose frame width:   size.x > 0.0f: custom  /  size.x < 0.0f or -FLT_MIN: right-align   /  size.x = 0.0f (default): use current ItemWidth
780
    // - Choose frame height:  size.y > 0.0f: custom  /  size.y < 0.0f or -FLT_MIN: bottom-align  /  size.y = 0.0f (default): arbitrary default height which can fit ~7 items
781
    IMGUI_API bool          BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region
782
    IMGUI_API void          EndListBox();                                                       // only call EndListBox() if BeginListBox() returned true!
783
    IMGUI_API bool          ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);
784
    IMGUI_API bool          ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1);
785
786
    // Widgets: Data Plotting
787
    // - Consider using ImPlot (https://github.com/epezent/implot) which is much better!
788
    IMGUI_API void          PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
789
    IMGUI_API void          PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));
790
    IMGUI_API void          PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
791
    IMGUI_API void          PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));
792
793
    // Widgets: Value() Helpers.
794
    // - Those are merely shortcut to calling Text() with a format string. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace)
795
    IMGUI_API void          Value(const char* prefix, bool b);
796
    IMGUI_API void          Value(const char* prefix, int v);
797
    IMGUI_API void          Value(const char* prefix, unsigned int v);
798
    IMGUI_API void          Value(const char* prefix, float v, const char* float_format = NULL);
799
800
    // Widgets: Menus
801
    // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar.
802
    // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it.
803
    // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it.
804
    // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.
805
    IMGUI_API bool          BeginMenuBar();                                                     // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).
806
    IMGUI_API void          EndMenuBar();                                                       // only call EndMenuBar() if BeginMenuBar() returns true!
807
    IMGUI_API bool          BeginMainMenuBar();                                                 // create and append to a full screen menu-bar.
808
    IMGUI_API void          EndMainMenuBar();                                                   // only call EndMainMenuBar() if BeginMainMenuBar() returns true!
809
    IMGUI_API bool          BeginMenu(const char* label, bool enabled = true);                  // create a sub-menu entry. only call EndMenu() if this returns true!
810
    IMGUI_API void          EndMenu();                                                          // only call EndMenu() if BeginMenu() returns true!
811
    IMGUI_API bool          MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true);  // return true when activated.
812
    IMGUI_API bool          MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true);              // return true when activated + toggle (*p_selected) if p_selected != NULL
813
814
    // Tooltips
815
    // - Tooltips are windows following the mouse. They do not take focus away.
816
    // - A tooltip window can contain items of any types.
817
    // - SetTooltip() is more or less a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom (with a subtlety that it discard any previously submitted tooltip)
818
    IMGUI_API bool          BeginTooltip();                                                     // begin/append a tooltip window.
819
    IMGUI_API void          EndTooltip();                                                       // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true!
820
    IMGUI_API void          SetTooltip(const char* fmt, ...) IM_FMTARGS(1);                     // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip().
821
    IMGUI_API void          SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);
822
823
    // Tooltips: helpers for showing a tooltip when hovering an item
824
    // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip) && BeginTooltip())' idiom.
825
    // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom.
826
    // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.
827
    IMGUI_API bool          BeginItemTooltip();                                                 // begin/append a tooltip window if preceding item was hovered.
828
    IMGUI_API void          SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1);                 // set a text-only tooltip if preceding item was hovered. override any previous call to SetTooltip().
829
    IMGUI_API void          SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);
830
831
    // Popups, Modals
832
    //  - They block normal mouse hovering detection (and therefore most mouse interactions) behind them.
833
    //  - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
834
    //  - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls.
835
    //  - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time.
836
    //  - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered().
837
    //  - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack.
838
    //    This is sometimes leading to confusing mistakes. May rework this in the future.
839
    //  - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards if returned true. ImGuiWindowFlags are forwarded to the window.
840
    //  - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar.
841
    IMGUI_API bool          BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0);                         // return true if the popup is open, and you can start outputting to it.
842
    IMGUI_API bool          BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it.
843
    IMGUI_API void          EndPopup();                                                                         // only call EndPopup() if BeginPopupXXX() returns true!
844
845
    // Popups: open/close functions
846
    //  - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options.
847
    //  - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
848
    //  - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually.
849
    //  - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options).
850
    //  - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup().
851
    //  - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened.
852
    //  - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter
853
    IMGUI_API void          OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0);                     // call to mark popup as open (don't call every frame!).
854
    IMGUI_API void          OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0);                             // id overload to facilitate calling from nested stacks
855
    IMGUI_API void          OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);   // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)
856
    IMGUI_API void          CloseCurrentPopup();                                                                // manually close the popup we have begin-ed into.
857
858
    // Popups: open+begin combined functions helpers
859
    //  - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking.
860
    //  - They are convenient to easily create context menus, hence the name.
861
    //  - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future.
862
    //  - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight.
863
    IMGUI_API bool          BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);  // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!
864
    IMGUI_API bool          BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window.
865
    IMGUI_API bool          BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);  // open+begin popup when clicked in void (where there are no windows).
866
867
    // Popups: query functions
868
    //  - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack.
869
    //  - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack.
870
    //  - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open.
871
    IMGUI_API bool          IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0);                         // return true if the popup is open.
872
873
    // Tables
874
    // - Full-featured replacement for old Columns API.
875
    // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary.
876
    // - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags.
877
    // The typical call flow is:
878
    // - 1. Call BeginTable(), early out if returning false.
879
    // - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults.
880
    // - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows.
881
    // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data.
882
    // - 5. Populate contents:
883
    //    - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column.
884
    //    - If you are using tables as a sort of grid, where every column is holding the same type of contents,
885
    //      you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex().
886
    //      TableNextColumn() will automatically wrap-around into the next row if needed.
887
    //    - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column!
888
    //    - Summary of possible call flow:
889
    //        - TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1")  // OK
890
    //        - TableNextRow() -> TableNextColumn()      -> Text("Hello 0") -> TableNextColumn()      -> Text("Hello 1")  // OK
891
    //        -                   TableNextColumn()      -> Text("Hello 0") -> TableNextColumn()      -> Text("Hello 1")  // OK: TableNextColumn() automatically gets to next row!
892
    //        - TableNextRow()                           -> Text("Hello 0")                                               // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!
893
    // - 5. Call EndTable()
894
    IMGUI_API bool          BeginTable(const char* str_id, int columns, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f);
895
    IMGUI_API void          EndTable();                                         // only call EndTable() if BeginTable() returns true!
896
    IMGUI_API void          TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row.
897
    IMGUI_API bool          TableNextColumn();                                  // append into the next column (or first column of next row if currently in last column). Return true when column is visible.
898
    IMGUI_API bool          TableSetColumnIndex(int column_n);                  // append into the specified column. Return true when column is visible.
899
900
    // Tables: Headers & Columns declaration
901
    // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.
902
    // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column.
903
    //   Headers are required to perform: reordering, sorting, and opening the context menu.
904
    //   The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody.
905
    // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in
906
    //   some advanced use cases (e.g. adding custom widgets in header row).
907
    // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled.
908
    IMGUI_API void          TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);
909
    IMGUI_API void          TableSetupScrollFreeze(int cols, int rows);         // lock columns/rows so they stay visible when scrolled.
910
    IMGUI_API void          TableHeader(const char* label);                     // submit one header cell manually (rarely used)
911
    IMGUI_API void          TableHeadersRow();                                  // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu
912
    IMGUI_API void          TableAngledHeadersRow();                            // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW.
913
914
    // Tables: Sorting & Miscellaneous functions
915
    // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.
916
    //   When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have
917
    //   changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,
918
    //   else you may wastefully sort your data every frame!
919
    // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index.
920
    IMGUI_API ImGuiTableSortSpecs*  TableGetSortSpecs();                        // get latest sort specs for the table (NULL if not sorting).  Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().
921
    IMGUI_API int                   TableGetColumnCount();                      // return number of columns (value passed to BeginTable)
922
    IMGUI_API int                   TableGetColumnIndex();                      // return current column index.
923
    IMGUI_API int                   TableGetRowIndex();                         // return current row index (header rows are accounted for)
924
    IMGUI_API const char*           TableGetColumnName(int column_n = -1);      // return "" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.
925
    IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1);     // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column.
926
    IMGUI_API void                  TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)
927
    IMGUI_API int                   TableGetHoveredColumn();                    // return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. Can also use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead.
928
    IMGUI_API void                  TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1);  // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.
929
930
    // Legacy Columns API (prefer using Tables!)
931
    // - You can also use SameLine(pos_x) to mimic simplified columns.
932
    IMGUI_API void          Columns(int count = 1, const char* id = NULL, bool borders = true);
933
    IMGUI_API void          NextColumn();                                                       // next column, defaults to current row or next row if the current row is finished
934
    IMGUI_API int           GetColumnIndex();                                                   // get current column index
935
    IMGUI_API float         GetColumnWidth(int column_index = -1);                              // get column width (in pixels). pass -1 to use current column
936
    IMGUI_API void          SetColumnWidth(int column_index, float width);                      // set column width (in pixels). pass -1 to use current column
937
    IMGUI_API float         GetColumnOffset(int column_index = -1);                             // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is typically 0.0f
938
    IMGUI_API void          SetColumnOffset(int column_index, float offset_x);                  // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column
939
    IMGUI_API int           GetColumnsCount();
940
941
    // Tab Bars, Tabs
942
    // - Note: Tabs are automatically created by the docking system (when in 'docking' branch). Use this to create tab bars/tabs yourself.
943
    IMGUI_API bool          BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);        // create and append into a TabBar
944
    IMGUI_API void          EndTabBar();                                                        // only call EndTabBar() if BeginTabBar() returns true!
945
    IMGUI_API bool          BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected.
946
    IMGUI_API void          EndTabItem();                                                       // only call EndTabItem() if BeginTabItem() returns true!
947
    IMGUI_API bool          TabItemButton(const char* label, ImGuiTabItemFlags flags = 0);      // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar.
948
    IMGUI_API void          SetTabItemClosed(const char* tab_or_docked_window_label);           // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name.
949
950
    // Logging/Capture
951
    // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging.
952
    IMGUI_API void          LogToTTY(int auto_open_depth = -1);                                 // start logging to tty (stdout)
953
    IMGUI_API void          LogToFile(int auto_open_depth = -1, const char* filename = NULL);   // start logging to file
954
    IMGUI_API void          LogToClipboard(int auto_open_depth = -1);                           // start logging to OS clipboard
955
    IMGUI_API void          LogFinish();                                                        // stop logging (close file, etc.)
956
    IMGUI_API void          LogButtons();                                                       // helper to display buttons for logging to tty/file/clipboard
957
    IMGUI_API void          LogText(const char* fmt, ...) IM_FMTARGS(1);                        // pass text data straight to log (without being displayed)
958
    IMGUI_API void          LogTextV(const char* fmt, va_list args) IM_FMTLIST(1);
959
960
    // Drag and Drop
961
    // - On source items, call BeginDragDropSource(), if it returns true also call SetDragDropPayload() + EndDragDropSource().
962
    // - On target candidates, call BeginDragDropTarget(), if it returns true also call AcceptDragDropPayload() + EndDragDropTarget().
963
    // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback "..." tooltip, see #1725)
964
    // - An item can be both drag source and drop target.
965
    IMGUI_API bool          BeginDragDropSource(ImGuiDragDropFlags flags = 0);                                      // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource()
966
    IMGUI_API bool          SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0);  // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted.
967
    IMGUI_API void          EndDragDropSource();                                                                    // only call EndDragDropSource() if BeginDragDropSource() returns true!
968
    IMGUI_API bool                  BeginDragDropTarget();                                                          // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget()
969
    IMGUI_API const ImGuiPayload*   AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0);          // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released.
970
    IMGUI_API void                  EndDragDropTarget();                                                            // only call EndDragDropTarget() if BeginDragDropTarget() returns true!
971
    IMGUI_API const ImGuiPayload*   GetDragDropPayload();                                                           // peek directly into the current payload from anywhere. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type.
972
973
    // Disabling [BETA API]
974
    // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)
975
    // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)
976
    // - Tooltips windows by exception are opted out of disabling.
977
    // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls)
978
    IMGUI_API void          BeginDisabled(bool disabled = true);
979
    IMGUI_API void          EndDisabled();
980
981
    // Clipping
982
    // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only.
983
    IMGUI_API void          PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect);
984
    IMGUI_API void          PopClipRect();
985
986
    // Focus, Activation
987
    IMGUI_API void          SetItemDefaultFocus();                                              // make last item the default focused item of a newly appearing window.
988
    IMGUI_API void          SetKeyboardFocusHere(int offset = 0);                               // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget.
989
990
    // Keyboard/Gamepad Navigation
991
    IMGUI_API void          SetNavCursorVisible(bool visible);                                  // alter visibility of keyboard/gamepad cursor. by default: show when using an arrow key, hide when clicking with mouse.
992
993
    // Overlapping mode
994
    IMGUI_API void          SetNextItemAllowOverlap();                                          // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this.
995
996
    // Item/Widgets Utilities and Query Functions
997
    // - Most of the functions are referring to the previous Item that has been submitted.
998
    // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions.
999
    IMGUI_API bool          IsItemHovered(ImGuiHoveredFlags flags = 0);                         // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options.
1000
    IMGUI_API bool          IsItemActive();                                                     // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false)
1001
    IMGUI_API bool          IsItemFocused();                                                    // is the last item focused for keyboard/gamepad navigation?
1002
    IMGUI_API bool          IsItemClicked(ImGuiMouseButton mouse_button = 0);                   // is the last item hovered and mouse clicked on? (**)  == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this is NOT equivalent to the behavior of e.g. Button(). Read comments in function definition.
1003
    IMGUI_API bool          IsItemVisible();                                                    // is the last item visible? (items may be out of sight because of clipping/scrolling)
1004
    IMGUI_API bool          IsItemEdited();                                                     // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets.
1005
    IMGUI_API bool          IsItemActivated();                                                  // was the last item just made active (item was previously inactive).
1006
    IMGUI_API bool          IsItemDeactivated();                                                // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing.
1007
    IMGUI_API bool          IsItemDeactivatedAfterEdit();                                       // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item).
1008
    IMGUI_API bool          IsItemToggledOpen();                                                // was the last item open state toggled? set by TreeNode().
1009
    IMGUI_API bool          IsAnyItemHovered();                                                 // is any item hovered?
1010
    IMGUI_API bool          IsAnyItemActive();                                                  // is any item active?
1011
    IMGUI_API bool          IsAnyItemFocused();                                                 // is any item focused?
1012
    IMGUI_API ImGuiID       GetItemID();                                                        // get ID of last item (~~ often same ImGui::GetID(label) beforehand)
1013
    IMGUI_API ImVec2        GetItemRectMin();                                                   // get upper-left bounding rectangle of the last item (screen space)
1014
    IMGUI_API ImVec2        GetItemRectMax();                                                   // get lower-right bounding rectangle of the last item (screen space)
1015
    IMGUI_API ImVec2        GetItemRectSize();                                                  // get size of last item
1016
1017
    // Viewports
1018
    // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
1019
    // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.
1020
    // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode.
1021
    IMGUI_API ImGuiViewport* GetMainViewport();                                                 // return primary/default viewport. This can never be NULL.
1022
1023
    // Background/Foreground Draw Lists
1024
    IMGUI_API ImDrawList*   GetBackgroundDrawList();                                            // this draw list will be the first rendered one. Useful to quickly draw shapes/text behind dear imgui contents.
1025
    IMGUI_API ImDrawList*   GetForegroundDrawList();                                            // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
1026
1027
    // Miscellaneous Utilities
1028
    IMGUI_API bool          IsRectVisible(const ImVec2& size);                                  // test if rectangle (of given size, starting from cursor position) is visible / not clipped.
1029
    IMGUI_API bool          IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max);      // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side.
1030
    IMGUI_API double        GetTime();                                                          // get global imgui time. incremented by io.DeltaTime every frame.
1031
    IMGUI_API int           GetFrameCount();                                                    // get global imgui frame count. incremented by 1 every frame.
1032
    IMGUI_API ImDrawListSharedData* GetDrawListSharedData();                                    // you may use this when creating your own ImDrawList instances.
1033
    IMGUI_API const char*   GetStyleColorName(ImGuiCol idx);                                    // get a string corresponding to the enum value (for display, saving, etc.).
1034
    IMGUI_API void          SetStateStorage(ImGuiStorage* storage);                             // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it)
1035
    IMGUI_API ImGuiStorage* GetStateStorage();
1036
1037
    // Text Utilities
1038
    IMGUI_API ImVec2        CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);
1039
1040
    // Color Utilities
1041
    IMGUI_API ImVec4        ColorConvertU32ToFloat4(ImU32 in);
1042
    IMGUI_API ImU32         ColorConvertFloat4ToU32(const ImVec4& in);
1043
    IMGUI_API void          ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v);
1044
    IMGUI_API void          ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b);
1045
1046
    // Inputs Utilities: Keyboard/Mouse/Gamepad
1047
    // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...).
1048
    // - (legacy: before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. This was obsoleted in 1.87 (2022-02) and completely removed in 1.91.5 (2024-11). See https://github.com/ocornut/imgui/issues/4921)
1049
    // - (legacy: any use of ImGuiKey will assert when key < 512 to detect passing legacy native/user indices)
1050
    IMGUI_API bool          IsKeyDown(ImGuiKey key);                                            // is key being held.
1051
    IMGUI_API bool          IsKeyPressed(ImGuiKey key, bool repeat = true);                     // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate
1052
    IMGUI_API bool          IsKeyReleased(ImGuiKey key);                                        // was key released (went from Down to !Down)?
1053
    IMGUI_API bool          IsKeyChordPressed(ImGuiKeyChord key_chord);                         // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead.
1054
    IMGUI_API int           GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate);  // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate
1055
    IMGUI_API const char*   GetKeyName(ImGuiKey key);                                           // [DEBUG] returns English name of the key. Those names are provided for debugging purpose and are not meant to be saved persistently nor compared.
1056
    IMGUI_API void          SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard);        // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call.
1057
1058
    // Inputs Utilities: Shortcut Testing & Routing [BETA]
1059
    // - ImGuiKeyChord = a ImGuiKey + optional ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.
1060
    //       ImGuiKey_C                          // Accepted by functions taking ImGuiKey or ImGuiKeyChord arguments
1061
    //       ImGuiMod_Ctrl | ImGuiKey_C          // Accepted by functions taking ImGuiKeyChord arguments
1062
    //   only ImGuiMod_XXX values are legal to combine with an ImGuiKey. You CANNOT combine two ImGuiKey values.
1063
    // - The general idea is that several callers may register interest in a shortcut, and only one owner gets it.
1064
    //      Parent   -> call Shortcut(Ctrl+S)    // When Parent is focused, Parent gets the shortcut.
1065
    //        Child1 -> call Shortcut(Ctrl+S)    // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts)
1066
    //        Child2 -> no call                  // When Child2 is focused, Parent gets the shortcut.
1067
    //   The whole system is order independent, so if Child1 makes its calls before Parent, results will be identical.
1068
    //   This is an important property as it facilitate working with foreign code or larger codebase.
1069
    // - To understand the difference:
1070
    //   - IsKeyChordPressed() compares mods and call IsKeyPressed() -> function has no side-effect.
1071
    //   - Shortcut() submits a route, routes are resolved, if it currently can be routed it calls IsKeyChordPressed() -> function has (desirable) side-effects as it can prevents another call from getting the route.
1072
    // - Visualize registered routes in 'Metrics/Debugger->Inputs'.
1073
    IMGUI_API bool          Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0);
1074
    IMGUI_API void          SetNextItemShortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags = 0);
1075
1076
    // Inputs Utilities: Key/Input Ownership [BETA]
1077
    // - One common use case would be to allow your items to disable standard inputs behaviors such
1078
    //   as Tab or Alt key handling, Mouse Wheel scrolling, etc.
1079
    //   e.g. Button(...); SetItemKeyOwner(ImGuiKey_MouseWheelY); to make hovering/activating a button disable wheel for scrolling.
1080
    // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them.
1081
    // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version.
1082
    IMGUI_API void          SetItemKeyOwner(ImGuiKey key);                                      // Set key owner to last item ID if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
1083
1084
    // Inputs Utilities: Mouse
1085
    // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right.
1086
    // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle.
1087
    // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold')
1088
    IMGUI_API bool          IsMouseDown(ImGuiMouseButton button);                               // is mouse button held?
1089
    IMGUI_API bool          IsMouseClicked(ImGuiMouseButton button, bool repeat = false);       // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1.
1090
    IMGUI_API bool          IsMouseReleased(ImGuiMouseButton button);                           // did mouse button released? (went from Down to !Down)
1091
    IMGUI_API bool          IsMouseDoubleClicked(ImGuiMouseButton button);                      // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true)
1092
    IMGUI_API bool          IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay);     // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename.
1093
    IMGUI_API int           GetMouseClickedCount(ImGuiMouseButton button);                      // return the number of successive mouse-clicks at the time where a click happen (otherwise 0).
1094
    IMGUI_API bool          IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.
1095
    IMGUI_API bool          IsMousePosValid(const ImVec2* mouse_pos = NULL);                    // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available
1096
    IMGUI_API bool          IsAnyMouseDown();                                                   // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.
1097
    IMGUI_API ImVec2        GetMousePos();                                                      // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls
1098
    IMGUI_API ImVec2        GetMousePosOnOpeningCurrentPopup();                                 // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves)
1099
    IMGUI_API bool          IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f);         // is mouse dragging? (uses io.MouseDraggingThreshold if lock_threshold < 0.0f)
1100
    IMGUI_API ImVec2        GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f);   // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (uses io.MouseDraggingThreshold if lock_threshold < 0.0f)
1101
    IMGUI_API void          ResetMouseDragDelta(ImGuiMouseButton button = 0);                   //
1102
    IMGUI_API ImGuiMouseCursor GetMouseCursor();                                                // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you
1103
    IMGUI_API void          SetMouseCursor(ImGuiMouseCursor cursor_type);                       // set desired mouse cursor shape
1104
    IMGUI_API void          SetNextFrameWantCaptureMouse(bool want_capture_mouse);              // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instructs your app to ignore inputs). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse;" after the next NewFrame() call.
1105
1106
    // Clipboard Utilities
1107
    // - Also see the LogToClipboard() function to capture GUI into clipboard, or easily output text data to the clipboard.
1108
    IMGUI_API const char*   GetClipboardText();
1109
    IMGUI_API void          SetClipboardText(const char* text);
1110
1111
    // Settings/.Ini Utilities
1112
    // - The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini").
1113
    // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.
1114
    // - Important: default value "imgui.ini" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).
1115
    IMGUI_API void          LoadIniSettingsFromDisk(const char* ini_filename);                  // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).
1116
    IMGUI_API void          LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.
1117
    IMGUI_API void          SaveIniSettingsToDisk(const char* ini_filename);                    // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).
1118
    IMGUI_API const char*   SaveIniSettingsToMemory(size_t* out_ini_size = NULL);               // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.
1119
1120
    // Debug Utilities
1121
    // - Your main debugging friend is the ShowMetricsWindow() function, which is also accessible from Demo->Tools->Metrics Debugger
1122
    IMGUI_API void          DebugTextEncoding(const char* text);
1123
    IMGUI_API void          DebugFlashStyleColor(ImGuiCol idx);
1124
    IMGUI_API void          DebugStartItemPicker();
1125
    IMGUI_API bool          DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro.
1126
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
1127
    IMGUI_API void          DebugLog(const char* fmt, ...)           IM_FMTARGS(1); // Call via IMGUI_DEBUG_LOG() for maximum stripping in caller code!
1128
    IMGUI_API void          DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1);
1129
#endif
1130
1131
    // Memory Allocators
1132
    // - Those functions are not reliant on the current context.
1133
    // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
1134
    //   for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
1135
    IMGUI_API void          SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL);
1136
    IMGUI_API void          GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data);
1137
    IMGUI_API void*         MemAlloc(size_t size);
1138
    IMGUI_API void          MemFree(void* ptr);
1139
1140
} // namespace ImGui
1141
1142
//-----------------------------------------------------------------------------
1143
// [SECTION] Flags & Enumerations
1144
//-----------------------------------------------------------------------------
1145
1146
// Flags for ImGui::Begin()
1147
// (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly)
1148
enum ImGuiWindowFlags_
1149
{
1150
    ImGuiWindowFlags_None                   = 0,
1151
    ImGuiWindowFlags_NoTitleBar             = 1 << 0,   // Disable title-bar
1152
    ImGuiWindowFlags_NoResize               = 1 << 1,   // Disable user resizing with the lower-right grip
1153
    ImGuiWindowFlags_NoMove                 = 1 << 2,   // Disable user moving the window
1154
    ImGuiWindowFlags_NoScrollbar            = 1 << 3,   // Disable scrollbars (window can still scroll with mouse or programmatically)
1155
    ImGuiWindowFlags_NoScrollWithMouse      = 1 << 4,   // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set.
1156
    ImGuiWindowFlags_NoCollapse             = 1 << 5,   // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node).
1157
    ImGuiWindowFlags_AlwaysAutoResize       = 1 << 6,   // Resize every window to its content every frame
1158
    ImGuiWindowFlags_NoBackground           = 1 << 7,   // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f).
1159
    ImGuiWindowFlags_NoSavedSettings        = 1 << 8,   // Never load/save settings in .ini file
1160
    ImGuiWindowFlags_NoMouseInputs          = 1 << 9,   // Disable catching mouse, hovering test with pass through.
1161
    ImGuiWindowFlags_MenuBar                = 1 << 10,  // Has a menu-bar
1162
    ImGuiWindowFlags_HorizontalScrollbar    = 1 << 11,  // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section.
1163
    ImGuiWindowFlags_NoFocusOnAppearing     = 1 << 12,  // Disable taking focus when transitioning from hidden to visible state
1164
    ImGuiWindowFlags_NoBringToFrontOnFocus  = 1 << 13,  // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)
1165
    ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14,  // Always show vertical scrollbar (even if ContentSize.y < Size.y)
1166
    ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15,  // Always show horizontal scrollbar (even if ContentSize.x < Size.x)
1167
    ImGuiWindowFlags_NoNavInputs            = 1 << 16,  // No keyboard/gamepad navigation within the window
1168
    ImGuiWindowFlags_NoNavFocus             = 1 << 17,  // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by CTRL+TAB)
1169
    ImGuiWindowFlags_UnsavedDocument        = 1 << 18,  // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
1170
    ImGuiWindowFlags_NoNav                  = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
1171
    ImGuiWindowFlags_NoDecoration           = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,
1172
    ImGuiWindowFlags_NoInputs               = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,
1173
1174
    // [Internal]
1175
    ImGuiWindowFlags_ChildWindow            = 1 << 24,  // Don't use! For internal use by BeginChild()
1176
    ImGuiWindowFlags_Tooltip                = 1 << 25,  // Don't use! For internal use by BeginTooltip()
1177
    ImGuiWindowFlags_Popup                  = 1 << 26,  // Don't use! For internal use by BeginPopup()
1178
    ImGuiWindowFlags_Modal                  = 1 << 27,  // Don't use! For internal use by BeginPopupModal()
1179
    ImGuiWindowFlags_ChildMenu              = 1 << 28,  // Don't use! For internal use by BeginMenu()
1180
1181
    // Obsolete names
1182
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1183
    ImGuiWindowFlags_NavFlattened           = 1 << 29,  // Obsoleted in 1.90.9: Use ImGuiChildFlags_NavFlattened in BeginChild() call.
1184
    ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30,  // Obsoleted in 1.90.0: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call.
1185
#endif
1186
};
1187
1188
// Flags for ImGui::BeginChild()
1189
// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Borders to be backward compatible with old API using 'bool border = false'.)
1190
// About using AutoResizeX/AutoResizeY flags:
1191
// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints").
1192
// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.
1193
//   - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped.
1194
//     While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional "resizing after becoming visible again" glitch.
1195
//   - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view.
1196
//     HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping.
1197
enum ImGuiChildFlags_
1198
{
1199
    ImGuiChildFlags_None                    = 0,
1200
    ImGuiChildFlags_Borders                 = 1 << 0,   // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason)
1201
    ImGuiChildFlags_AlwaysUseWindowPadding  = 1 << 1,   // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)
1202
    ImGuiChildFlags_ResizeX                 = 1 << 2,   // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags)
1203
    ImGuiChildFlags_ResizeY                 = 1 << 3,   // Allow resize from bottom border (layout direction). "
1204
    ImGuiChildFlags_AutoResizeX             = 1 << 4,   // Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above.
1205
    ImGuiChildFlags_AutoResizeY             = 1 << 5,   // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above.
1206
    ImGuiChildFlags_AlwaysAutoResize        = 1 << 6,   // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.
1207
    ImGuiChildFlags_FrameStyle              = 1 << 7,   // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.
1208
    ImGuiChildFlags_NavFlattened            = 1 << 8,   // [BETA] Share focus scope, allow keyboard/gamepad navigation to cross over parent border to this child or between sibling child windows.
1209
1210
    // Obsolete names
1211
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1212
    ImGuiChildFlags_Border                  = ImGuiChildFlags_Borders,  // Renamed in 1.91.1 (August 2024) for consistency.
1213
#endif
1214
};
1215
1216
// Flags for ImGui::PushItemFlag()
1217
// (Those are shared by all items)
1218
enum ImGuiItemFlags_
1219
{
1220
    ImGuiItemFlags_None                     = 0,        // (Default)
1221
    ImGuiItemFlags_NoTabStop                = 1 << 0,   // false    // Disable keyboard tabbing. This is a "lighter" version of ImGuiItemFlags_NoNav.
1222
    ImGuiItemFlags_NoNav                    = 1 << 1,   // false    // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls).
1223
    ImGuiItemFlags_NoNavDefaultFocus        = 1 << 2,   // false    // Disable item being a candidate for default focus (e.g. used by title bar items).
1224
    ImGuiItemFlags_ButtonRepeat             = 1 << 3,   // false    // Any button-like behavior will have repeat mode enabled (based on io.KeyRepeatDelay and io.KeyRepeatRate values). Note that you can also call IsItemActive() after any button to tell if it is being held.
1225
    ImGuiItemFlags_AutoClosePopups          = 1 << 4,   // true     // MenuItem()/Selectable() automatically close their parent popup window.
1226
    ImGuiItemFlags_AllowDuplicateId         = 1 << 5,   // false    // Allow submitting an item with the same identifier as an item already submitted this frame without triggering a warning tooltip if io.ConfigDebugHighlightIdConflicts is set.
1227
};
1228
1229
// Flags for ImGui::InputText()
1230
// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive)
1231
enum ImGuiInputTextFlags_
1232
{
1233
    // Basic filters (also see ImGuiInputTextFlags_CallbackCharFilter)
1234
    ImGuiInputTextFlags_None                = 0,
1235
    ImGuiInputTextFlags_CharsDecimal        = 1 << 0,   // Allow 0123456789.+-*/
1236
    ImGuiInputTextFlags_CharsHexadecimal    = 1 << 1,   // Allow 0123456789ABCDEFabcdef
1237
    ImGuiInputTextFlags_CharsScientific     = 1 << 2,   // Allow 0123456789.+-*/eE (Scientific notation input)
1238
    ImGuiInputTextFlags_CharsUppercase      = 1 << 3,   // Turn a..z into A..Z
1239
    ImGuiInputTextFlags_CharsNoBlank        = 1 << 4,   // Filter out spaces, tabs
1240
1241
    // Inputs
1242
    ImGuiInputTextFlags_AllowTabInput       = 1 << 5,   // Pressing TAB input a '\t' character into the text field
1243
    ImGuiInputTextFlags_EnterReturnsTrue    = 1 << 6,   // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider using IsItemDeactivatedAfterEdit() instead!
1244
    ImGuiInputTextFlags_EscapeClearsAll     = 1 << 7,   // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert)
1245
    ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 8,   // In multi-line mode, validate with Enter, add new line with Ctrl+Enter (default is opposite: validate with Ctrl+Enter, add line with Enter).
1246
1247
    // Other options
1248
    ImGuiInputTextFlags_ReadOnly            = 1 << 9,   // Read-only mode
1249
    ImGuiInputTextFlags_Password            = 1 << 10,  // Password mode, display all characters as '*', disable copy
1250
    ImGuiInputTextFlags_AlwaysOverwrite     = 1 << 11,  // Overwrite mode
1251
    ImGuiInputTextFlags_AutoSelectAll       = 1 << 12,  // Select entire text when first taking mouse focus
1252
    ImGuiInputTextFlags_ParseEmptyRefVal    = 1 << 13,  // InputFloat(), InputInt(), InputScalar() etc. only: parse empty string as zero value.
1253
    ImGuiInputTextFlags_DisplayEmptyRefVal  = 1 << 14,  // InputFloat(), InputInt(), InputScalar() etc. only: when value is zero, do not display it. Generally used with ImGuiInputTextFlags_ParseEmptyRefVal.
1254
    ImGuiInputTextFlags_NoHorizontalScroll  = 1 << 15,  // Disable following the cursor horizontally
1255
    ImGuiInputTextFlags_NoUndoRedo          = 1 << 16,  // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().
1256
1257
    // Elide display / Alignment
1258
    ImGuiInputTextFlags_ElideLeft           = 1 << 17,  // When text doesn't fit, elide left side to ensure right side stays visible. Useful for path/filenames. Single-line only!
1259
1260
    // Callback features
1261
    ImGuiInputTextFlags_CallbackCompletion  = 1 << 18,  // Callback on pressing TAB (for completion handling)
1262
    ImGuiInputTextFlags_CallbackHistory     = 1 << 19,  // Callback on pressing Up/Down arrows (for history handling)
1263
    ImGuiInputTextFlags_CallbackAlways      = 1 << 20,  // Callback on each iteration. User code may query cursor position, modify text buffer.
1264
    ImGuiInputTextFlags_CallbackCharFilter  = 1 << 21,  // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
1265
    ImGuiInputTextFlags_CallbackResize      = 1 << 22,  // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)
1266
    ImGuiInputTextFlags_CallbackEdit        = 1 << 23,  // Callback on any edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.
1267
1268
    // Multi-line Word-Wrapping [BETA]
1269
    // - Not well tested yet. Please report any incorrect cursor movement, selection behavior etc. bug to https://github.com/ocornut/imgui/issues/3237.
1270
    // - Wrapping style is not ideal. Wrapping of long words/sections (e.g. words larger than total available width) may be particularly unpleasing.
1271
    // - Wrapping width needs to always account for the possibility of a vertical scrollbar.
1272
    // - It is much slower than regular text fields.
1273
    //   Ballpark estimate of cost on my 2019 desktop PC: for a 100 KB text buffer: +~0.3 ms (Optimized) / +~1.0 ms (Debug build).
1274
    //   The CPU cost is very roughly proportional to text length, so a 10 KB buffer should cost about ten times less.
1275
    ImGuiInputTextFlags_WordWrap            = 1 << 24,  // InputTextMultine(): word-wrap lines that are too long.
1276
1277
    // Obsolete names
1278
    //ImGuiInputTextFlags_AlwaysInsertMode  = ImGuiInputTextFlags_AlwaysOverwrite   // [renamed in 1.82] name was not matching behavior
1279
};
1280
1281
// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*()
1282
enum ImGuiTreeNodeFlags_
1283
{
1284
    ImGuiTreeNodeFlags_None                 = 0,
1285
    ImGuiTreeNodeFlags_Selected             = 1 << 0,   // Draw as selected
1286
    ImGuiTreeNodeFlags_Framed               = 1 << 1,   // Draw frame with background (e.g. for CollapsingHeader)
1287
    ImGuiTreeNodeFlags_AllowOverlap         = 1 << 2,   // Hit testing to allow subsequent widgets to overlap this one
1288
    ImGuiTreeNodeFlags_NoTreePushOnOpen     = 1 << 3,   // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack
1289
    ImGuiTreeNodeFlags_NoAutoOpenOnLog      = 1 << 4,   // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes)
1290
    ImGuiTreeNodeFlags_DefaultOpen          = 1 << 5,   // Default node to be open
1291
    ImGuiTreeNodeFlags_OpenOnDoubleClick    = 1 << 6,   // Open on double-click instead of simple click (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined.
1292
    ImGuiTreeNodeFlags_OpenOnArrow          = 1 << 7,   // Open when clicking on the arrow part (default for multi-select unless any _OpenOnXXX behavior is set explicitly). Both behaviors may be combined.
1293
    ImGuiTreeNodeFlags_Leaf                 = 1 << 8,   // No collapsing, no arrow (use as a convenience for leaf nodes).
1294
    ImGuiTreeNodeFlags_Bullet               = 1 << 9,   // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag!
1295
    ImGuiTreeNodeFlags_FramePadding         = 1 << 10,  // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding() before the node.
1296
    ImGuiTreeNodeFlags_SpanAvailWidth       = 1 << 11,  // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line without using AllowOverlap mode.
1297
    ImGuiTreeNodeFlags_SpanFullWidth        = 1 << 12,  // Extend hit box to the left-most and right-most edges (cover the indent area).
1298
    ImGuiTreeNodeFlags_SpanLabelWidth       = 1 << 13,  // Narrow hit box + narrow hovering highlight, will only cover the label text.
1299
    ImGuiTreeNodeFlags_SpanAllColumns       = 1 << 14,  // Frame will span all columns of its container table (label will still fit in current column)
1300
    ImGuiTreeNodeFlags_LabelSpanAllColumns  = 1 << 15,  // Label will span all columns of its container table
1301
    //ImGuiTreeNodeFlags_NoScrollOnOpen     = 1 << 16,  // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible
1302
    ImGuiTreeNodeFlags_NavLeftJumpsToParent = 1 << 17,  // Nav: left arrow moves back to parent. This is processed in TreePop() when there's an unfullfilled Left nav request remaining.
1303
    ImGuiTreeNodeFlags_CollapsingHeader     = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,
1304
1305
    // [EXPERIMENTAL] Draw lines connecting TreeNode hierarchy. Discuss in GitHub issue #2920.
1306
    // Default value is pulled from style.TreeLinesFlags. May be overridden in TreeNode calls.
1307
    ImGuiTreeNodeFlags_DrawLinesNone        = 1 << 18,  // No lines drawn
1308
    ImGuiTreeNodeFlags_DrawLinesFull        = 1 << 19,  // Horizontal lines to child nodes. Vertical line drawn down to TreePop() position: cover full contents. Faster (for large trees).
1309
    ImGuiTreeNodeFlags_DrawLinesToNodes     = 1 << 20,  // Horizontal lines to child nodes. Vertical line drawn down to bottom-most child node. Slower (for large trees).
1310
1311
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1312
    ImGuiTreeNodeFlags_NavLeftJumpsBackHere = ImGuiTreeNodeFlags_NavLeftJumpsToParent,  // Renamed in 1.92.0
1313
    ImGuiTreeNodeFlags_SpanTextWidth        = ImGuiTreeNodeFlags_SpanLabelWidth,        // Renamed in 1.90.7
1314
    //ImGuiTreeNodeFlags_AllowItemOverlap   = ImGuiTreeNodeFlags_AllowOverlap,          // Renamed in 1.89.7
1315
#endif
1316
};
1317
1318
// Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions.
1319
// - To be backward compatible with older API which took an 'int mouse_button = 1' argument instead of 'ImGuiPopupFlags flags',
1320
//   we need to treat small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags.
1321
//   It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags.
1322
// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0.
1323
//   IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter
1324
//   and want to use another flag, you need to pass in the ImGuiPopupFlags_MouseButtonRight flag explicitly.
1325
// - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later).
1326
enum ImGuiPopupFlags_
1327
{
1328
    ImGuiPopupFlags_None                    = 0,
1329
    ImGuiPopupFlags_MouseButtonLeft         = 0,        // For BeginPopupContext*(): open on Left Mouse release. Guaranteed to always be == 0 (same as ImGuiMouseButton_Left)
1330
    ImGuiPopupFlags_MouseButtonRight        = 1,        // For BeginPopupContext*(): open on Right Mouse release. Guaranteed to always be == 1 (same as ImGuiMouseButton_Right)
1331
    ImGuiPopupFlags_MouseButtonMiddle       = 2,        // For BeginPopupContext*(): open on Middle Mouse release. Guaranteed to always be == 2 (same as ImGuiMouseButton_Middle)
1332
    ImGuiPopupFlags_MouseButtonMask_        = 0x1F,
1333
    ImGuiPopupFlags_MouseButtonDefault_     = 1,
1334
    ImGuiPopupFlags_NoReopen                = 1 << 5,   // For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation)
1335
    //ImGuiPopupFlags_NoReopenAlwaysNavInit = 1 << 6,   // For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening.
1336
    ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7,   // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack
1337
    ImGuiPopupFlags_NoOpenOverItems         = 1 << 8,   // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space
1338
    ImGuiPopupFlags_AnyPopupId              = 1 << 10,  // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.
1339
    ImGuiPopupFlags_AnyPopupLevel           = 1 << 11,  // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)
1340
    ImGuiPopupFlags_AnyPopup                = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel,
1341
};
1342
1343
// Flags for ImGui::Selectable()
1344
enum ImGuiSelectableFlags_
1345
{
1346
    ImGuiSelectableFlags_None               = 0,
1347
    ImGuiSelectableFlags_NoAutoClosePopups  = 1 << 0,   // Clicking this doesn't close parent popup window (overrides ImGuiItemFlags_AutoClosePopups)
1348
    ImGuiSelectableFlags_SpanAllColumns     = 1 << 1,   // Frame will span all columns of its container table (text will still fit in current column)
1349
    ImGuiSelectableFlags_AllowDoubleClick   = 1 << 2,   // Generate press events on double clicks too
1350
    ImGuiSelectableFlags_Disabled           = 1 << 3,   // Cannot be selected, display grayed out text
1351
    ImGuiSelectableFlags_AllowOverlap       = 1 << 4,   // (WIP) Hit testing to allow subsequent widgets to overlap this one
1352
    ImGuiSelectableFlags_Highlight          = 1 << 5,   // Make the item be displayed as if it is hovered
1353
    ImGuiSelectableFlags_SelectOnNav        = 1 << 6,   // Auto-select when moved into, unless Ctrl is held. Automatic when in a BeginMultiSelect() block.
1354
1355
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1356
    ImGuiSelectableFlags_DontClosePopups    = ImGuiSelectableFlags_NoAutoClosePopups,   // Renamed in 1.91.0
1357
    //ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap,        // Renamed in 1.89.7
1358
#endif
1359
};
1360
1361
// Flags for ImGui::BeginCombo()
1362
enum ImGuiComboFlags_
1363
{
1364
    ImGuiComboFlags_None                    = 0,
1365
    ImGuiComboFlags_PopupAlignLeft          = 1 << 0,   // Align the popup toward the left by default
1366
    ImGuiComboFlags_HeightSmall             = 1 << 1,   // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo()
1367
    ImGuiComboFlags_HeightRegular           = 1 << 2,   // Max ~8 items visible (default)
1368
    ImGuiComboFlags_HeightLarge             = 1 << 3,   // Max ~20 items visible
1369
    ImGuiComboFlags_HeightLargest           = 1 << 4,   // As many fitting items as possible
1370
    ImGuiComboFlags_NoArrowButton           = 1 << 5,   // Display on the preview box without the square arrow button
1371
    ImGuiComboFlags_NoPreview               = 1 << 6,   // Display only a square arrow button
1372
    ImGuiComboFlags_WidthFitPreview         = 1 << 7,   // Width dynamically calculated from preview contents
1373
    ImGuiComboFlags_HeightMask_             = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest,
1374
};
1375
1376
// Flags for ImGui::BeginTabBar()
1377
enum ImGuiTabBarFlags_
1378
{
1379
    ImGuiTabBarFlags_None                           = 0,
1380
    ImGuiTabBarFlags_Reorderable                    = 1 << 0,   // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list
1381
    ImGuiTabBarFlags_AutoSelectNewTabs              = 1 << 1,   // Automatically select new tabs when they appear
1382
    ImGuiTabBarFlags_TabListPopupButton             = 1 << 2,   // Disable buttons to open the tab list popup
1383
    ImGuiTabBarFlags_NoCloseWithMiddleMouseButton   = 1 << 3,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
1384
    ImGuiTabBarFlags_NoTabListScrollingButtons      = 1 << 4,   // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll)
1385
    ImGuiTabBarFlags_NoTooltip                      = 1 << 5,   // Disable tooltips when hovering a tab
1386
    ImGuiTabBarFlags_DrawSelectedOverline           = 1 << 6,   // Draw selected overline markers over selected tab
1387
1388
    // Fitting/Resize policy
1389
    ImGuiTabBarFlags_FittingPolicyMixed             = 1 << 7,   // Shrink down tabs when they don't fit, until width is style.TabMinWidthShrink, then enable scrolling buttons.
1390
    ImGuiTabBarFlags_FittingPolicyShrink            = 1 << 8,   // Shrink down tabs when they don't fit
1391
    ImGuiTabBarFlags_FittingPolicyScroll            = 1 << 9,   // Enable scrolling buttons when tabs don't fit
1392
    ImGuiTabBarFlags_FittingPolicyMask_             = ImGuiTabBarFlags_FittingPolicyMixed | ImGuiTabBarFlags_FittingPolicyShrink | ImGuiTabBarFlags_FittingPolicyScroll,
1393
    ImGuiTabBarFlags_FittingPolicyDefault_          = ImGuiTabBarFlags_FittingPolicyMixed,
1394
1395
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1396
    ImGuiTabBarFlags_FittingPolicyResizeDown        = ImGuiTabBarFlags_FittingPolicyShrink, // Renamed in 1.92.2
1397
#endif
1398
};
1399
1400
// Flags for ImGui::BeginTabItem()
1401
enum ImGuiTabItemFlags_
1402
{
1403
    ImGuiTabItemFlags_None                          = 0,
1404
    ImGuiTabItemFlags_UnsavedDocument               = 1 << 0,   // Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure.
1405
    ImGuiTabItemFlags_SetSelected                   = 1 << 1,   // Trigger flag to programmatically make the tab selected when calling BeginTabItem()
1406
    ImGuiTabItemFlags_NoCloseWithMiddleMouseButton  = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
1407
    ImGuiTabItemFlags_NoPushId                      = 1 << 3,   // Don't call PushID()/PopID() on BeginTabItem()/EndTabItem()
1408
    ImGuiTabItemFlags_NoTooltip                     = 1 << 4,   // Disable tooltip for the given tab
1409
    ImGuiTabItemFlags_NoReorder                     = 1 << 5,   // Disable reordering this tab or having another tab cross over this tab
1410
    ImGuiTabItemFlags_Leading                       = 1 << 6,   // Enforce the tab position to the left of the tab bar (after the tab list popup button)
1411
    ImGuiTabItemFlags_Trailing                      = 1 << 7,   // Enforce the tab position to the right of the tab bar (before the scrolling buttons)
1412
    ImGuiTabItemFlags_NoAssumedClosure              = 1 << 8,   // Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
1413
};
1414
1415
// Flags for ImGui::IsWindowFocused()
1416
enum ImGuiFocusedFlags_
1417
{
1418
    ImGuiFocusedFlags_None                          = 0,
1419
    ImGuiFocusedFlags_ChildWindows                  = 1 << 0,   // Return true if any children of the window is focused
1420
    ImGuiFocusedFlags_RootWindow                    = 1 << 1,   // Test from root window (top most parent of the current hierarchy)
1421
    ImGuiFocusedFlags_AnyWindow                     = 1 << 2,   // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!
1422
    ImGuiFocusedFlags_NoPopupHierarchy              = 1 << 3,   // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
1423
    //ImGuiFocusedFlags_DockHierarchy               = 1 << 4,   // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
1424
    ImGuiFocusedFlags_RootAndChildWindows           = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows,
1425
};
1426
1427
// Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered()
1428
// Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ!
1429
// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls.
1430
enum ImGuiHoveredFlags_
1431
{
1432
    ImGuiHoveredFlags_None                          = 0,        // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them.
1433
    ImGuiHoveredFlags_ChildWindows                  = 1 << 0,   // IsWindowHovered() only: Return true if any children of the window is hovered
1434
    ImGuiHoveredFlags_RootWindow                    = 1 << 1,   // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)
1435
    ImGuiHoveredFlags_AnyWindow                     = 1 << 2,   // IsWindowHovered() only: Return true if any window is hovered
1436
    ImGuiHoveredFlags_NoPopupHierarchy              = 1 << 3,   // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
1437
    //ImGuiHoveredFlags_DockHierarchy               = 1 << 4,   // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
1438
    ImGuiHoveredFlags_AllowWhenBlockedByPopup       = 1 << 5,   // Return true even if a popup window is normally blocking access to this item/window
1439
    //ImGuiHoveredFlags_AllowWhenBlockedByModal     = 1 << 6,   // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
1440
    ImGuiHoveredFlags_AllowWhenBlockedByActiveItem  = 1 << 7,   // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
1441
    ImGuiHoveredFlags_AllowWhenOverlappedByItem     = 1 << 8,   // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item.
1442
    ImGuiHoveredFlags_AllowWhenOverlappedByWindow   = 1 << 9,   // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window.
1443
    ImGuiHoveredFlags_AllowWhenDisabled             = 1 << 10,  // IsItemHovered() only: Return true even if the item is disabled
1444
    ImGuiHoveredFlags_NoNavOverride                 = 1 << 11,  // IsItemHovered() only: Disable using keyboard/gamepad navigation state when active, always query mouse
1445
    ImGuiHoveredFlags_AllowWhenOverlapped           = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow,
1446
    ImGuiHoveredFlags_RectOnly                      = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
1447
    ImGuiHoveredFlags_RootAndChildWindows           = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,
1448
1449
    // Tooltips mode
1450
    // - typically used in IsItemHovered() + SetTooltip() sequence.
1451
    // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.
1452
    //   e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.
1453
    // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often.
1454
    // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.
1455
    ImGuiHoveredFlags_ForTooltip                    = 1 << 12,  // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.
1456
1457
    // (Advanced) Mouse Hovering delays.
1458
    // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags.
1459
    // - use those if you need specific overrides.
1460
    ImGuiHoveredFlags_Stationary                    = 1 << 13,  // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.
1461
    ImGuiHoveredFlags_DelayNone                     = 1 << 14,  // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this.
1462
    ImGuiHoveredFlags_DelayShort                    = 1 << 15,  // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
1463
    ImGuiHoveredFlags_DelayNormal                   = 1 << 16,  // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
1464
    ImGuiHoveredFlags_NoSharedDelay                 = 1 << 17,  // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)
1465
};
1466
1467
// Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload()
1468
enum ImGuiDragDropFlags_
1469
{
1470
    ImGuiDragDropFlags_None                         = 0,
1471
    // BeginDragDropSource() flags
1472
    ImGuiDragDropFlags_SourceNoPreviewTooltip       = 1 << 0,   // Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior.
1473
    ImGuiDragDropFlags_SourceNoDisableHover         = 1 << 1,   // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item.
1474
    ImGuiDragDropFlags_SourceNoHoldToOpenOthers     = 1 << 2,   // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item.
1475
    ImGuiDragDropFlags_SourceAllowNullID            = 1 << 3,   // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit.
1476
    ImGuiDragDropFlags_SourceExtern                 = 1 << 4,   // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously.
1477
    ImGuiDragDropFlags_PayloadAutoExpire            = 1 << 5,   // Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged)
1478
    ImGuiDragDropFlags_PayloadNoCrossContext        = 1 << 6,   // Hint to specify that the payload may not be copied outside current dear imgui context.
1479
    ImGuiDragDropFlags_PayloadNoCrossProcess        = 1 << 7,   // Hint to specify that the payload may not be copied outside current process.
1480
    // AcceptDragDropPayload() flags
1481
    ImGuiDragDropFlags_AcceptBeforeDelivery         = 1 << 10,  // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered.
1482
    ImGuiDragDropFlags_AcceptNoDrawDefaultRect      = 1 << 11,  // Do not draw the default highlight rectangle when hovering over target.
1483
    ImGuiDragDropFlags_AcceptNoPreviewTooltip       = 1 << 12,  // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site.
1484
    ImGuiDragDropFlags_AcceptPeekOnly               = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery.
1485
1486
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1487
    ImGuiDragDropFlags_SourceAutoExpirePayload = ImGuiDragDropFlags_PayloadAutoExpire, // Renamed in 1.90.9
1488
#endif
1489
};
1490
1491
// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui.
1492
0
#define IMGUI_PAYLOAD_TYPE_COLOR_3F     "_COL3F"    // float[3]: Standard type for colors, without alpha. User code may use this type.
1493
0
#define IMGUI_PAYLOAD_TYPE_COLOR_4F     "_COL4F"    // float[4]: Standard type for colors. User code may use this type.
1494
1495
// A primary data type
1496
enum ImGuiDataType_
1497
{
1498
    ImGuiDataType_S8,       // signed char / char (with sensible compilers)
1499
    ImGuiDataType_U8,       // unsigned char
1500
    ImGuiDataType_S16,      // short
1501
    ImGuiDataType_U16,      // unsigned short
1502
    ImGuiDataType_S32,      // int
1503
    ImGuiDataType_U32,      // unsigned int
1504
    ImGuiDataType_S64,      // long long / __int64
1505
    ImGuiDataType_U64,      // unsigned long long / unsigned __int64
1506
    ImGuiDataType_Float,    // float
1507
    ImGuiDataType_Double,   // double
1508
    ImGuiDataType_Bool,     // bool (provided for user convenience, not supported by scalar widgets)
1509
    ImGuiDataType_String,   // char* (provided for user convenience, not supported by scalar widgets)
1510
    ImGuiDataType_COUNT
1511
};
1512
1513
// A cardinal direction
1514
enum ImGuiDir : int
1515
{
1516
    ImGuiDir_None    = -1,
1517
    ImGuiDir_Left    = 0,
1518
    ImGuiDir_Right   = 1,
1519
    ImGuiDir_Up      = 2,
1520
    ImGuiDir_Down    = 3,
1521
    ImGuiDir_COUNT
1522
};
1523
1524
// A sorting direction
1525
enum ImGuiSortDirection : ImU8
1526
{
1527
    ImGuiSortDirection_None         = 0,
1528
    ImGuiSortDirection_Ascending    = 1,    // Ascending = 0->9, A->Z etc.
1529
    ImGuiSortDirection_Descending   = 2     // Descending = 9->0, Z->A etc.
1530
};
1531
1532
// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values.
1533
// All our named keys are >= 512. Keys value 0 to 511 are left unused and were legacy native/opaque key values (< 1.87).
1534
// Support for legacy keys was completely removed in 1.91.5.
1535
// Read details about the 1.87+ transition : https://github.com/ocornut/imgui/issues/4921
1536
// Note that "Keys" related to physical keys and are not the same concept as input "Characters", the later are submitted via io.AddInputCharacter().
1537
// The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps.
1538
enum ImGuiKey : int
1539
{
1540
    // Keyboard
1541
    ImGuiKey_None = 0,
1542
    ImGuiKey_NamedKey_BEGIN = 512,  // First valid key value (other than 0)
1543
1544
    ImGuiKey_Tab = 512,             // == ImGuiKey_NamedKey_BEGIN
1545
    ImGuiKey_LeftArrow,
1546
    ImGuiKey_RightArrow,
1547
    ImGuiKey_UpArrow,
1548
    ImGuiKey_DownArrow,
1549
    ImGuiKey_PageUp,
1550
    ImGuiKey_PageDown,
1551
    ImGuiKey_Home,
1552
    ImGuiKey_End,
1553
    ImGuiKey_Insert,
1554
    ImGuiKey_Delete,
1555
    ImGuiKey_Backspace,
1556
    ImGuiKey_Space,
1557
    ImGuiKey_Enter,
1558
    ImGuiKey_Escape,
1559
    ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper,     // Also see ImGuiMod_Ctrl, ImGuiMod_Shift, ImGuiMod_Alt, ImGuiMod_Super below!
1560
    ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper,
1561
    ImGuiKey_Menu,
1562
    ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9,
1563
    ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J,
1564
    ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T,
1565
    ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z,
1566
    ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6,
1567
    ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12,
1568
    ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18,
1569
    ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24,
1570
    ImGuiKey_Apostrophe,        // '
1571
    ImGuiKey_Comma,             // ,
1572
    ImGuiKey_Minus,             // -
1573
    ImGuiKey_Period,            // .
1574
    ImGuiKey_Slash,             // /
1575
    ImGuiKey_Semicolon,         // ;
1576
    ImGuiKey_Equal,             // =
1577
    ImGuiKey_LeftBracket,       // [
1578
    ImGuiKey_Backslash,         // \ (this text inhibit multiline comment caused by backslash)
1579
    ImGuiKey_RightBracket,      // ]
1580
    ImGuiKey_GraveAccent,       // `
1581
    ImGuiKey_CapsLock,
1582
    ImGuiKey_ScrollLock,
1583
    ImGuiKey_NumLock,
1584
    ImGuiKey_PrintScreen,
1585
    ImGuiKey_Pause,
1586
    ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4,
1587
    ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9,
1588
    ImGuiKey_KeypadDecimal,
1589
    ImGuiKey_KeypadDivide,
1590
    ImGuiKey_KeypadMultiply,
1591
    ImGuiKey_KeypadSubtract,
1592
    ImGuiKey_KeypadAdd,
1593
    ImGuiKey_KeypadEnter,
1594
    ImGuiKey_KeypadEqual,
1595
    ImGuiKey_AppBack,               // Available on some keyboard/mouses. Often referred as "Browser Back"
1596
    ImGuiKey_AppForward,
1597
    ImGuiKey_Oem102,                // Non-US backslash.
1598
1599
    // Gamepad
1600
    // (analog values are 0.0f to 1.0f)
1601
    // (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets)
1602
    //                              // XBOX        | SWITCH  | PLAYSTA. | -> ACTION
1603
    ImGuiKey_GamepadStart,          // Menu        | +       | Options  |
1604
    ImGuiKey_GamepadBack,           // View        | -       | Share    |
1605
    ImGuiKey_GamepadFaceLeft,       // X           | Y       | Square   | Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows)
1606
    ImGuiKey_GamepadFaceRight,      // B           | A       | Circle   | Cancel / Close / Exit
1607
    ImGuiKey_GamepadFaceUp,         // Y           | X       | Triangle | Text Input / On-screen Keyboard
1608
    ImGuiKey_GamepadFaceDown,       // A           | B       | Cross    | Activate / Open / Toggle / Tweak
1609
    ImGuiKey_GamepadDpadLeft,       // D-pad Left  | "       | "        | Move / Tweak / Resize Window (in Windowing mode)
1610
    ImGuiKey_GamepadDpadRight,      // D-pad Right | "       | "        | Move / Tweak / Resize Window (in Windowing mode)
1611
    ImGuiKey_GamepadDpadUp,         // D-pad Up    | "       | "        | Move / Tweak / Resize Window (in Windowing mode)
1612
    ImGuiKey_GamepadDpadDown,       // D-pad Down  | "       | "        | Move / Tweak / Resize Window (in Windowing mode)
1613
    ImGuiKey_GamepadL1,             // L Bumper    | L       | L1       | Tweak Slower / Focus Previous (in Windowing mode)
1614
    ImGuiKey_GamepadR1,             // R Bumper    | R       | R1       | Tweak Faster / Focus Next (in Windowing mode)
1615
    ImGuiKey_GamepadL2,             // L Trigger   | ZL      | L2       | [Analog]
1616
    ImGuiKey_GamepadR2,             // R Trigger   | ZR      | R2       | [Analog]
1617
    ImGuiKey_GamepadL3,             // L Stick     | L3      | L3       |
1618
    ImGuiKey_GamepadR3,             // R Stick     | R3      | R3       |
1619
    ImGuiKey_GamepadLStickLeft,     //             |         |          | [Analog] Move Window (in Windowing mode)
1620
    ImGuiKey_GamepadLStickRight,    //             |         |          | [Analog] Move Window (in Windowing mode)
1621
    ImGuiKey_GamepadLStickUp,       //             |         |          | [Analog] Move Window (in Windowing mode)
1622
    ImGuiKey_GamepadLStickDown,     //             |         |          | [Analog] Move Window (in Windowing mode)
1623
    ImGuiKey_GamepadRStickLeft,     //             |         |          | [Analog]
1624
    ImGuiKey_GamepadRStickRight,    //             |         |          | [Analog]
1625
    ImGuiKey_GamepadRStickUp,       //             |         |          | [Analog]
1626
    ImGuiKey_GamepadRStickDown,     //             |         |          | [Analog]
1627
1628
    // Aliases: Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls)
1629
    // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API.
1630
    ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY,
1631
1632
    // [Internal] Reserved for mod storage
1633
    ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper,
1634
1635
    // [Internal] If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END.
1636
    ImGuiKey_NamedKey_END,
1637
    ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,
1638
1639
    // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)
1640
    // - Any functions taking a ImGuiKeyChord parameter can binary-or those with regular keys, e.g. Shortcut(ImGuiMod_Ctrl | ImGuiKey_S).
1641
    // - Those are written back into io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper for convenience,
1642
    //   but may be accessed via standard key API such as IsKeyPressed(), IsKeyReleased(), querying duration etc.
1643
    // - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those
1644
    //   and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiMod_Ctrl).
1645
    // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.
1646
    //   In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and
1647
    //   backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...
1648
    // - On macOS, we swap Cmd(Super) and Ctrl keys at the time of the io.AddKeyEvent() call.
1649
    ImGuiMod_None                   = 0,
1650
    ImGuiMod_Ctrl                   = 1 << 12, // Ctrl (non-macOS), Cmd (macOS)
1651
    ImGuiMod_Shift                  = 1 << 13, // Shift
1652
    ImGuiMod_Alt                    = 1 << 14, // Option/Menu
1653
    ImGuiMod_Super                  = 1 << 15, // Windows/Super (non-macOS), Ctrl (macOS)
1654
    ImGuiMod_Mask_                  = 0xF000,  // 4-bits
1655
1656
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1657
    ImGuiKey_COUNT                  = ImGuiKey_NamedKey_END,    // Obsoleted in 1.91.5 because it was extremely misleading (since named keys don't start at 0 anymore)
1658
    ImGuiMod_Shortcut               = ImGuiMod_Ctrl,            // Removed in 1.90.7, you can now simply use ImGuiMod_Ctrl
1659
    ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89
1660
    //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter,              // Renamed in 1.87
1661
#endif
1662
};
1663
1664
// Flags for Shortcut(), SetNextItemShortcut(),
1665
// (and for upcoming extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() that are still in imgui_internal.h)
1666
// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function)
1667
enum ImGuiInputFlags_
1668
{
1669
    ImGuiInputFlags_None                    = 0,
1670
    ImGuiInputFlags_Repeat                  = 1 << 0,   // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.
1671
1672
    // Flags for Shortcut(), SetNextItemShortcut()
1673
    // - Routing policies: RouteGlobal+OverActive >> RouteActive or RouteFocused (if owner is active item) >> RouteGlobal+OverFocused >> RouteFocused (if in focused window stack) >> RouteGlobal.
1674
    // - Default policy is RouteFocused. Can select only 1 policy among all available.
1675
    ImGuiInputFlags_RouteActive             = 1 << 10,  // Route to active item only.
1676
    ImGuiInputFlags_RouteFocused            = 1 << 11,  // Route to windows in the focus stack (DEFAULT). Deep-most focused window takes inputs. Active item takes inputs over deep-most focused window.
1677
    ImGuiInputFlags_RouteGlobal             = 1 << 12,  // Global route (unless a focused window or active item registered the route).
1678
    ImGuiInputFlags_RouteAlways             = 1 << 13,  // Do not register route, poll keys directly.
1679
    // - Routing options
1680
    ImGuiInputFlags_RouteOverFocused        = 1 << 14,  // Option: global route: higher priority than focused route (unless active item in focused route).
1681
    ImGuiInputFlags_RouteOverActive         = 1 << 15,  // Option: global route: higher priority than active item. Unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overridden by this. May not be fully honored as user/internal code is likely to always assume they can access keys when active.
1682
    ImGuiInputFlags_RouteUnlessBgFocused    = 1 << 16,  // Option: global route: will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.
1683
    ImGuiInputFlags_RouteFromRootWindow     = 1 << 17,  // Option: route evaluated from the point of view of root window rather than current window.
1684
1685
    // Flags for SetNextItemShortcut()
1686
    ImGuiInputFlags_Tooltip                 = 1 << 18,  // Automatically display a tooltip when hovering item [BETA] Unsure of right api (opt-in/opt-out)
1687
};
1688
1689
// Configuration flags stored in io.ConfigFlags. Set by user/application.
1690
enum ImGuiConfigFlags_
1691
{
1692
    ImGuiConfigFlags_None                   = 0,
1693
    ImGuiConfigFlags_NavEnableKeyboard      = 1 << 0,   // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + space/enter to activate.
1694
    ImGuiConfigFlags_NavEnableGamepad       = 1 << 1,   // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad.
1695
    ImGuiConfigFlags_NoMouse                = 1 << 4,   // Instruct dear imgui to disable mouse inputs and interactions.
1696
    ImGuiConfigFlags_NoMouseCursorChange    = 1 << 5,   // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead.
1697
    ImGuiConfigFlags_NoKeyboard             = 1 << 6,   // Instruct dear imgui to disable keyboard inputs and interactions. This is done by ignoring keyboard events and clearing existing states.
1698
1699
    // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui)
1700
    ImGuiConfigFlags_IsSRGB                 = 1 << 20,  // Application is SRGB-aware.
1701
    ImGuiConfigFlags_IsTouchScreen          = 1 << 21,  // Application is using a touch screen instead of a mouse.
1702
1703
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1704
    ImGuiConfigFlags_NavEnableSetMousePos   = 1 << 2,   // [moved/renamed in 1.91.4] -> use bool io.ConfigNavMoveSetMousePos
1705
    ImGuiConfigFlags_NavNoCaptureKeyboard   = 1 << 3,   // [moved/renamed in 1.91.4] -> use bool io.ConfigNavCaptureKeyboard
1706
#endif
1707
};
1708
1709
// Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend.
1710
enum ImGuiBackendFlags_
1711
{
1712
    ImGuiBackendFlags_None                  = 0,
1713
    ImGuiBackendFlags_HasGamepad            = 1 << 0,   // Backend Platform supports gamepad and currently has one connected.
1714
    ImGuiBackendFlags_HasMouseCursors       = 1 << 1,   // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape.
1715
    ImGuiBackendFlags_HasSetMousePos        = 1 << 2,   // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set).
1716
    ImGuiBackendFlags_RendererHasVtxOffset  = 1 << 3,   // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.
1717
    ImGuiBackendFlags_RendererHasTextures   = 1 << 4,   // Backend Renderer supports ImTextureData requests to create/update/destroy textures. This enables incremental texture updates and texture reloads. See https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md for instructions on how to upgrade your custom backend.
1718
};
1719
1720
// Enumeration for PushStyleColor() / PopStyleColor()
1721
enum ImGuiCol_
1722
{
1723
    ImGuiCol_Text,
1724
    ImGuiCol_TextDisabled,
1725
    ImGuiCol_WindowBg,              // Background of normal windows
1726
    ImGuiCol_ChildBg,               // Background of child windows
1727
    ImGuiCol_PopupBg,               // Background of popups, menus, tooltips windows
1728
    ImGuiCol_Border,
1729
    ImGuiCol_BorderShadow,
1730
    ImGuiCol_FrameBg,               // Background of checkbox, radio button, plot, slider, text input
1731
    ImGuiCol_FrameBgHovered,
1732
    ImGuiCol_FrameBgActive,
1733
    ImGuiCol_TitleBg,               // Title bar
1734
    ImGuiCol_TitleBgActive,         // Title bar when focused
1735
    ImGuiCol_TitleBgCollapsed,      // Title bar when collapsed
1736
    ImGuiCol_MenuBarBg,
1737
    ImGuiCol_ScrollbarBg,
1738
    ImGuiCol_ScrollbarGrab,
1739
    ImGuiCol_ScrollbarGrabHovered,
1740
    ImGuiCol_ScrollbarGrabActive,
1741
    ImGuiCol_CheckMark,             // Checkbox tick and RadioButton circle
1742
    ImGuiCol_SliderGrab,
1743
    ImGuiCol_SliderGrabActive,
1744
    ImGuiCol_Button,
1745
    ImGuiCol_ButtonHovered,
1746
    ImGuiCol_ButtonActive,
1747
    ImGuiCol_Header,                // Header* colors are used for CollapsingHeader, TreeNode, Selectable, MenuItem
1748
    ImGuiCol_HeaderHovered,
1749
    ImGuiCol_HeaderActive,
1750
    ImGuiCol_Separator,
1751
    ImGuiCol_SeparatorHovered,
1752
    ImGuiCol_SeparatorActive,
1753
    ImGuiCol_ResizeGrip,            // Resize grip in lower-right and lower-left corners of windows.
1754
    ImGuiCol_ResizeGripHovered,
1755
    ImGuiCol_ResizeGripActive,
1756
    ImGuiCol_InputTextCursor,       // InputText cursor/caret
1757
    ImGuiCol_TabHovered,            // Tab background, when hovered
1758
    ImGuiCol_Tab,                   // Tab background, when tab-bar is focused & tab is unselected
1759
    ImGuiCol_TabSelected,           // Tab background, when tab-bar is focused & tab is selected
1760
    ImGuiCol_TabSelectedOverline,   // Tab horizontal overline, when tab-bar is focused & tab is selected
1761
    ImGuiCol_TabDimmed,             // Tab background, when tab-bar is unfocused & tab is unselected
1762
    ImGuiCol_TabDimmedSelected,     // Tab background, when tab-bar is unfocused & tab is selected
1763
    ImGuiCol_TabDimmedSelectedOverline,//..horizontal overline, when tab-bar is unfocused & tab is selected
1764
    ImGuiCol_PlotLines,
1765
    ImGuiCol_PlotLinesHovered,
1766
    ImGuiCol_PlotHistogram,
1767
    ImGuiCol_PlotHistogramHovered,
1768
    ImGuiCol_TableHeaderBg,         // Table header background
1769
    ImGuiCol_TableBorderStrong,     // Table outer and header borders (prefer using Alpha=1.0 here)
1770
    ImGuiCol_TableBorderLight,      // Table inner borders (prefer using Alpha=1.0 here)
1771
    ImGuiCol_TableRowBg,            // Table row background (even rows)
1772
    ImGuiCol_TableRowBgAlt,         // Table row background (odd rows)
1773
    ImGuiCol_TextLink,              // Hyperlink color
1774
    ImGuiCol_TextSelectedBg,        // Selected text inside an InputText
1775
    ImGuiCol_TreeLines,             // Tree node hierarchy outlines when using ImGuiTreeNodeFlags_DrawLines
1776
    ImGuiCol_DragDropTarget,        // Rectangle highlighting a drop target
1777
    ImGuiCol_UnsavedMarker,         // Unsaved Document marker (in window title and tabs)
1778
    ImGuiCol_NavCursor,             // Color of keyboard/gamepad navigation cursor/rectangle, when visible
1779
    ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB
1780
    ImGuiCol_NavWindowingDimBg,     // Darken/colorize entire screen behind the CTRL+TAB window list, when active
1781
    ImGuiCol_ModalWindowDimBg,      // Darken/colorize entire screen behind a modal window, when one is active
1782
    ImGuiCol_COUNT,
1783
1784
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1785
    ImGuiCol_TabActive = ImGuiCol_TabSelected,                  // [renamed in 1.90.9]
1786
    ImGuiCol_TabUnfocused = ImGuiCol_TabDimmed,                 // [renamed in 1.90.9]
1787
    ImGuiCol_TabUnfocusedActive = ImGuiCol_TabDimmedSelected,   // [renamed in 1.90.9]
1788
    ImGuiCol_NavHighlight = ImGuiCol_NavCursor,                 // [renamed in 1.91.4]
1789
#endif
1790
};
1791
1792
// Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure.
1793
// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.
1794
//   During initialization or between frames, feel free to just poke into ImGuiStyle directly.
1795
// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.
1796
//   - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
1797
//   - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
1798
//   - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
1799
// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.
1800
enum ImGuiStyleVar_
1801
{
1802
    // Enum name -------------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions)
1803
    ImGuiStyleVar_Alpha,                    // float     Alpha
1804
    ImGuiStyleVar_DisabledAlpha,            // float     DisabledAlpha
1805
    ImGuiStyleVar_WindowPadding,            // ImVec2    WindowPadding
1806
    ImGuiStyleVar_WindowRounding,           // float     WindowRounding
1807
    ImGuiStyleVar_WindowBorderSize,         // float     WindowBorderSize
1808
    ImGuiStyleVar_WindowMinSize,            // ImVec2    WindowMinSize
1809
    ImGuiStyleVar_WindowTitleAlign,         // ImVec2    WindowTitleAlign
1810
    ImGuiStyleVar_ChildRounding,            // float     ChildRounding
1811
    ImGuiStyleVar_ChildBorderSize,          // float     ChildBorderSize
1812
    ImGuiStyleVar_PopupRounding,            // float     PopupRounding
1813
    ImGuiStyleVar_PopupBorderSize,          // float     PopupBorderSize
1814
    ImGuiStyleVar_FramePadding,             // ImVec2    FramePadding
1815
    ImGuiStyleVar_FrameRounding,            // float     FrameRounding
1816
    ImGuiStyleVar_FrameBorderSize,          // float     FrameBorderSize
1817
    ImGuiStyleVar_ItemSpacing,              // ImVec2    ItemSpacing
1818
    ImGuiStyleVar_ItemInnerSpacing,         // ImVec2    ItemInnerSpacing
1819
    ImGuiStyleVar_IndentSpacing,            // float     IndentSpacing
1820
    ImGuiStyleVar_CellPadding,              // ImVec2    CellPadding
1821
    ImGuiStyleVar_ScrollbarSize,            // float     ScrollbarSize
1822
    ImGuiStyleVar_ScrollbarRounding,        // float     ScrollbarRounding
1823
    ImGuiStyleVar_ScrollbarPadding,         // float     ScrollbarPadding
1824
    ImGuiStyleVar_GrabMinSize,              // float     GrabMinSize
1825
    ImGuiStyleVar_GrabRounding,             // float     GrabRounding
1826
    ImGuiStyleVar_ImageBorderSize,          // float     ImageBorderSize
1827
    ImGuiStyleVar_TabRounding,              // float     TabRounding
1828
    ImGuiStyleVar_TabBorderSize,            // float     TabBorderSize
1829
    ImGuiStyleVar_TabMinWidthBase,          // float     TabMinWidthBase
1830
    ImGuiStyleVar_TabMinWidthShrink,        // float     TabMinWidthShrink
1831
    ImGuiStyleVar_TabBarBorderSize,         // float     TabBarBorderSize
1832
    ImGuiStyleVar_TabBarOverlineSize,       // float     TabBarOverlineSize
1833
    ImGuiStyleVar_TableAngledHeadersAngle,  // float     TableAngledHeadersAngle
1834
    ImGuiStyleVar_TableAngledHeadersTextAlign,// ImVec2  TableAngledHeadersTextAlign
1835
    ImGuiStyleVar_TreeLinesSize,            // float     TreeLinesSize
1836
    ImGuiStyleVar_TreeLinesRounding,        // float     TreeLinesRounding
1837
    ImGuiStyleVar_ButtonTextAlign,          // ImVec2    ButtonTextAlign
1838
    ImGuiStyleVar_SelectableTextAlign,      // ImVec2    SelectableTextAlign
1839
    ImGuiStyleVar_SeparatorTextBorderSize,  // float     SeparatorTextBorderSize
1840
    ImGuiStyleVar_SeparatorTextAlign,       // ImVec2    SeparatorTextAlign
1841
    ImGuiStyleVar_SeparatorTextPadding,     // ImVec2    SeparatorTextPadding
1842
    ImGuiStyleVar_COUNT
1843
};
1844
1845
// Flags for InvisibleButton() [extended in imgui_internal.h]
1846
enum ImGuiButtonFlags_
1847
{
1848
    ImGuiButtonFlags_None                   = 0,
1849
    ImGuiButtonFlags_MouseButtonLeft        = 1 << 0,   // React on left mouse button (default)
1850
    ImGuiButtonFlags_MouseButtonRight       = 1 << 1,   // React on right mouse button
1851
    ImGuiButtonFlags_MouseButtonMiddle      = 1 << 2,   // React on center mouse button
1852
    ImGuiButtonFlags_MouseButtonMask_       = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, // [Internal]
1853
    ImGuiButtonFlags_EnableNav              = 1 << 3,   // InvisibleButton(): do not disable navigation/tabbing. Otherwise disabled by default.
1854
};
1855
1856
// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()
1857
enum ImGuiColorEditFlags_
1858
{
1859
    ImGuiColorEditFlags_None            = 0,
1860
    ImGuiColorEditFlags_NoAlpha         = 1 << 1,   //              // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer).
1861
    ImGuiColorEditFlags_NoPicker        = 1 << 2,   //              // ColorEdit: disable picker when clicking on color square.
1862
    ImGuiColorEditFlags_NoOptions       = 1 << 3,   //              // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview.
1863
    ImGuiColorEditFlags_NoSmallPreview  = 1 << 4,   //              // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs)
1864
    ImGuiColorEditFlags_NoInputs        = 1 << 5,   //              // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square).
1865
    ImGuiColorEditFlags_NoTooltip       = 1 << 6,   //              // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview.
1866
    ImGuiColorEditFlags_NoLabel         = 1 << 7,   //              // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker).
1867
    ImGuiColorEditFlags_NoSidePreview   = 1 << 8,   //              // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead.
1868
    ImGuiColorEditFlags_NoDragDrop      = 1 << 9,   //              // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source.
1869
    ImGuiColorEditFlags_NoBorder        = 1 << 10,  //              // ColorButton: disable border (which is enforced by default)
1870
1871
    // Alpha preview
1872
    // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview.
1873
    // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior.
1874
    // - The new flags may be combined better and allow finer controls.
1875
    ImGuiColorEditFlags_AlphaOpaque     = 1 << 11,  //              // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha.
1876
    ImGuiColorEditFlags_AlphaNoBg       = 1 << 12,  //              // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color.
1877
    ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13,  //              // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview.
1878
1879
    // User Options (right-click on widget to change some of them).
1880
    ImGuiColorEditFlags_AlphaBar        = 1 << 16,  //              // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.
1881
    ImGuiColorEditFlags_HDR             = 1 << 19,  //              // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well).
1882
    ImGuiColorEditFlags_DisplayRGB      = 1 << 20,  // [Display]    // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex.
1883
    ImGuiColorEditFlags_DisplayHSV      = 1 << 21,  // [Display]    // "
1884
    ImGuiColorEditFlags_DisplayHex      = 1 << 22,  // [Display]    // "
1885
    ImGuiColorEditFlags_Uint8           = 1 << 23,  // [DataType]   // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255.
1886
    ImGuiColorEditFlags_Float           = 1 << 24,  // [DataType]   // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers.
1887
    ImGuiColorEditFlags_PickerHueBar    = 1 << 25,  // [Picker]     // ColorPicker: bar for Hue, rectangle for Sat/Value.
1888
    ImGuiColorEditFlags_PickerHueWheel  = 1 << 26,  // [Picker]     // ColorPicker: wheel for Hue, triangle for Sat/Value.
1889
    ImGuiColorEditFlags_InputRGB        = 1 << 27,  // [Input]      // ColorEdit, ColorPicker: input and output data in RGB format.
1890
    ImGuiColorEditFlags_InputHSV        = 1 << 28,  // [Input]      // ColorEdit, ColorPicker: input and output data in HSV format.
1891
1892
    // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to
1893
    // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup.
1894
    ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,
1895
1896
    // [Internal] Masks
1897
    ImGuiColorEditFlags_AlphaMask_      = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf,
1898
    ImGuiColorEditFlags_DisplayMask_    = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex,
1899
    ImGuiColorEditFlags_DataTypeMask_   = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float,
1900
    ImGuiColorEditFlags_PickerMask_     = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar,
1901
    ImGuiColorEditFlags_InputMask_      = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV,
1902
1903
    // Obsolete names
1904
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1905
    ImGuiColorEditFlags_AlphaPreview = 0,         // [Removed in 1.91.8] This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set.
1906
#endif
1907
    //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex  // [renamed in 1.69]
1908
};
1909
1910
// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.
1911
// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.
1912
// (Those are per-item flags. There is shared behavior flag too: ImGuiIO: io.ConfigDragClickToInputText)
1913
enum ImGuiSliderFlags_
1914
{
1915
    ImGuiSliderFlags_None               = 0,
1916
    ImGuiSliderFlags_Logarithmic        = 1 << 5,       // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.
1917
    ImGuiSliderFlags_NoRoundToFormat    = 1 << 6,       // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits).
1918
    ImGuiSliderFlags_NoInput            = 1 << 7,       // Disable CTRL+Click or Enter key allowing to input text directly into the widget.
1919
    ImGuiSliderFlags_WrapAround         = 1 << 8,       // Enable wrapping around from max to min and from min to max. Only supported by DragXXX() functions for now.
1920
    ImGuiSliderFlags_ClampOnInput       = 1 << 9,       // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.
1921
    ImGuiSliderFlags_ClampZeroRange     = 1 << 10,      // Clamp even if min==max==0.0f. Otherwise due to legacy reason DragXXX functions don't clamp with those values. When your clamping limits are dynamic you almost always want to use it.
1922
    ImGuiSliderFlags_NoSpeedTweaks      = 1 << 11,      // Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.
1923
    ImGuiSliderFlags_AlwaysClamp        = ImGuiSliderFlags_ClampOnInput | ImGuiSliderFlags_ClampZeroRange,
1924
    ImGuiSliderFlags_InvalidMask_       = 0x7000000F,   // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.
1925
};
1926
1927
// Identify a mouse button.
1928
// Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience.
1929
enum ImGuiMouseButton_
1930
{
1931
    ImGuiMouseButton_Left = 0,
1932
    ImGuiMouseButton_Right = 1,
1933
    ImGuiMouseButton_Middle = 2,
1934
    ImGuiMouseButton_COUNT = 5
1935
};
1936
1937
// Enumeration for GetMouseCursor()
1938
// User code may request backend to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here
1939
enum ImGuiMouseCursor_
1940
{
1941
    ImGuiMouseCursor_None = -1,
1942
    ImGuiMouseCursor_Arrow = 0,
1943
    ImGuiMouseCursor_TextInput,         // When hovering over InputText, etc.
1944
    ImGuiMouseCursor_ResizeAll,         // (Unused by Dear ImGui functions)
1945
    ImGuiMouseCursor_ResizeNS,          // When hovering over a horizontal border
1946
    ImGuiMouseCursor_ResizeEW,          // When hovering over a vertical border or a column
1947
    ImGuiMouseCursor_ResizeNESW,        // When hovering over the bottom-left corner of a window
1948
    ImGuiMouseCursor_ResizeNWSE,        // When hovering over the bottom-right corner of a window
1949
    ImGuiMouseCursor_Hand,              // (Unused by Dear ImGui functions. Use for e.g. hyperlinks)
1950
    ImGuiMouseCursor_Wait,              // When waiting for something to process/load.
1951
    ImGuiMouseCursor_Progress,          // When waiting for something to process/load, but application is still interactive.
1952
    ImGuiMouseCursor_NotAllowed,        // When hovering something with disallowed interaction. Usually a crossed circle.
1953
    ImGuiMouseCursor_COUNT
1954
};
1955
1956
// Enumeration for AddMouseSourceEvent() actual source of Mouse Input data.
1957
// Historically we use "Mouse" terminology everywhere to indicate pointer data, e.g. MousePos, IsMousePressed(), io.AddMousePosEvent()
1958
// But that "Mouse" data can come from different source which occasionally may be useful for application to know about.
1959
// You can submit a change of pointer type using io.AddMouseSourceEvent().
1960
enum ImGuiMouseSource : int
1961
{
1962
    ImGuiMouseSource_Mouse = 0,         // Input is coming from an actual mouse.
1963
    ImGuiMouseSource_TouchScreen,       // Input is coming from a touch screen (no hovering prior to initial press, less precise initial press aiming, dual-axis wheeling possible).
1964
    ImGuiMouseSource_Pen,               // Input is coming from a pressure/magnetic pen (often used in conjunction with high-sampling rates).
1965
    ImGuiMouseSource_COUNT
1966
};
1967
1968
// Enumeration for ImGui::SetNextWindow***(), SetWindow***(), SetNextItem***() functions
1969
// Represent a condition.
1970
// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always.
1971
enum ImGuiCond_
1972
{
1973
    ImGuiCond_None          = 0,        // No condition (always set the variable), same as _Always
1974
    ImGuiCond_Always        = 1 << 0,   // No condition (always set the variable), same as _None
1975
    ImGuiCond_Once          = 1 << 1,   // Set the variable once per runtime session (only the first call will succeed)
1976
    ImGuiCond_FirstUseEver  = 1 << 2,   // Set the variable if the object/window has no persistently saved data (no entry in .ini file)
1977
    ImGuiCond_Appearing     = 1 << 3,   // Set the variable if the object/window is appearing after being hidden/inactive (or the first time)
1978
};
1979
1980
//-----------------------------------------------------------------------------
1981
// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)
1982
//-----------------------------------------------------------------------------
1983
1984
// Flags for ImGui::BeginTable()
1985
// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.
1986
//   Read comments/demos carefully + experiment with live demos to get acquainted with them.
1987
// - The DEFAULT sizing policies are:
1988
//    - Default to ImGuiTableFlags_SizingFixedFit    if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize.
1989
//    - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off.
1990
// - When ScrollX is off:
1991
//    - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight.
1992
//    - Columns sizing policy allowed: Stretch (default), Fixed/Auto.
1993
//    - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).
1994
//    - Stretch Columns will share the remaining width according to their respective weight.
1995
//    - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.
1996
//      The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
1997
//      (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
1998
// - When ScrollX is on:
1999
//    - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed
2000
//    - Columns sizing policy allowed: Fixed/Auto mostly.
2001
//    - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed.
2002
//    - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.
2003
//    - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().
2004
//      If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again.
2005
// - Read on documentation at the top of imgui_tables.cpp for details.
2006
enum ImGuiTableFlags_
2007
{
2008
    // Features
2009
    ImGuiTableFlags_None                       = 0,
2010
    ImGuiTableFlags_Resizable                  = 1 << 0,   // Enable resizing columns.
2011
    ImGuiTableFlags_Reorderable                = 1 << 1,   // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)
2012
    ImGuiTableFlags_Hideable                   = 1 << 2,   // Enable hiding/disabling columns in context menu.
2013
    ImGuiTableFlags_Sortable                   = 1 << 3,   // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate.
2014
    ImGuiTableFlags_NoSavedSettings            = 1 << 4,   // Disable persisting columns order, width and sort settings in the .ini file.
2015
    ImGuiTableFlags_ContextMenuInBody          = 1 << 5,   // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().
2016
    // Decorations
2017
    ImGuiTableFlags_RowBg                      = 1 << 6,   // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)
2018
    ImGuiTableFlags_BordersInnerH              = 1 << 7,   // Draw horizontal borders between rows.
2019
    ImGuiTableFlags_BordersOuterH              = 1 << 8,   // Draw horizontal borders at the top and bottom.
2020
    ImGuiTableFlags_BordersInnerV              = 1 << 9,   // Draw vertical borders between columns.
2021
    ImGuiTableFlags_BordersOuterV              = 1 << 10,  // Draw vertical borders on the left and right sides.
2022
    ImGuiTableFlags_BordersH                   = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.
2023
    ImGuiTableFlags_BordersV                   = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.
2024
    ImGuiTableFlags_BordersInner               = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.
2025
    ImGuiTableFlags_BordersOuter               = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.
2026
    ImGuiTableFlags_Borders                    = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter,   // Draw all borders.
2027
    ImGuiTableFlags_NoBordersInBody            = 1 << 11,  // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style
2028
    ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12,  // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style
2029
    // Sizing Policy (read above for defaults)
2030
    ImGuiTableFlags_SizingFixedFit             = 1 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.
2031
    ImGuiTableFlags_SizingFixedSame            = 2 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.
2032
    ImGuiTableFlags_SizingStretchProp          = 3 << 13,  // Columns default to _WidthStretch with default weights proportional to each columns contents widths.
2033
    ImGuiTableFlags_SizingStretchSame          = 4 << 13,  // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn().
2034
    // Sizing Extra Options
2035
    ImGuiTableFlags_NoHostExtendX              = 1 << 16,  // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
2036
    ImGuiTableFlags_NoHostExtendY              = 1 << 17,  // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.
2037
    ImGuiTableFlags_NoKeepColumnsVisible       = 1 << 18,  // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.
2038
    ImGuiTableFlags_PreciseWidths              = 1 << 19,  // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.
2039
    // Clipping
2040
    ImGuiTableFlags_NoClip                     = 1 << 20,  // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().
2041
    // Padding
2042
    ImGuiTableFlags_PadOuterX                  = 1 << 21,  // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers.
2043
    ImGuiTableFlags_NoPadOuterX                = 1 << 22,  // Default if BordersOuterV is off. Disable outermost padding.
2044
    ImGuiTableFlags_NoPadInnerX                = 1 << 23,  // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).
2045
    // Scrolling
2046
    ImGuiTableFlags_ScrollX                    = 1 << 24,  // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX.
2047
    ImGuiTableFlags_ScrollY                    = 1 << 25,  // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.
2048
    // Sorting
2049
    ImGuiTableFlags_SortMulti                  = 1 << 26,  // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).
2050
    ImGuiTableFlags_SortTristate               = 1 << 27,  // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).
2051
    // Miscellaneous
2052
    ImGuiTableFlags_HighlightHoveredColumn     = 1 << 28,  // Highlight column headers when hovered (may evolve into a fuller highlight)
2053
2054
    // [Internal] Combinations and masks
2055
    ImGuiTableFlags_SizingMask_                = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,
2056
};
2057
2058
// Flags for ImGui::TableSetupColumn()
2059
enum ImGuiTableColumnFlags_
2060
{
2061
    // Input configuration flags
2062
    ImGuiTableColumnFlags_None                  = 0,
2063
    ImGuiTableColumnFlags_Disabled              = 1 << 0,   // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state)
2064
    ImGuiTableColumnFlags_DefaultHide           = 1 << 1,   // Default as a hidden/disabled column.
2065
    ImGuiTableColumnFlags_DefaultSort           = 1 << 2,   // Default as a sorting column.
2066
    ImGuiTableColumnFlags_WidthStretch          = 1 << 3,   // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp).
2067
    ImGuiTableColumnFlags_WidthFixed            = 1 << 4,   // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).
2068
    ImGuiTableColumnFlags_NoResize              = 1 << 5,   // Disable manual resizing.
2069
    ImGuiTableColumnFlags_NoReorder             = 1 << 6,   // Disable manual reordering this column, this will also prevent other columns from crossing over this column.
2070
    ImGuiTableColumnFlags_NoHide                = 1 << 7,   // Disable ability to hide/disable this column.
2071
    ImGuiTableColumnFlags_NoClip                = 1 << 8,   // Disable clipping for this column (all NoClip columns will render in a same draw command).
2072
    ImGuiTableColumnFlags_NoSort                = 1 << 9,   // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).
2073
    ImGuiTableColumnFlags_NoSortAscending       = 1 << 10,  // Disable ability to sort in the ascending direction.
2074
    ImGuiTableColumnFlags_NoSortDescending      = 1 << 11,  // Disable ability to sort in the descending direction.
2075
    ImGuiTableColumnFlags_NoHeaderLabel         = 1 << 12,  // TableHeadersRow() will submit an empty label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. You may append into this cell by calling TableSetColumnIndex() right after the TableHeadersRow() call.
2076
    ImGuiTableColumnFlags_NoHeaderWidth         = 1 << 13,  // Disable header text width contribution to automatic column width.
2077
    ImGuiTableColumnFlags_PreferSortAscending   = 1 << 14,  // Make the initial sort direction Ascending when first sorting on this column (default).
2078
    ImGuiTableColumnFlags_PreferSortDescending  = 1 << 15,  // Make the initial sort direction Descending when first sorting on this column.
2079
    ImGuiTableColumnFlags_IndentEnable          = 1 << 16,  // Use current Indent value when entering cell (default for column 0).
2080
    ImGuiTableColumnFlags_IndentDisable         = 1 << 17,  // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.
2081
    ImGuiTableColumnFlags_AngledHeader          = 1 << 18,  // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row.
2082
2083
    // Output status flags, read-only via TableGetColumnFlags()
2084
    ImGuiTableColumnFlags_IsEnabled             = 1 << 24,  // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags.
2085
    ImGuiTableColumnFlags_IsVisible             = 1 << 25,  // Status: is visible == is enabled AND not clipped by scrolling.
2086
    ImGuiTableColumnFlags_IsSorted              = 1 << 26,  // Status: is currently part of the sort specs
2087
    ImGuiTableColumnFlags_IsHovered             = 1 << 27,  // Status: is hovered by mouse
2088
2089
    // [Internal] Combinations and masks
2090
    ImGuiTableColumnFlags_WidthMask_            = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed,
2091
    ImGuiTableColumnFlags_IndentMask_           = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,
2092
    ImGuiTableColumnFlags_StatusMask_           = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,
2093
    ImGuiTableColumnFlags_NoDirectResize_       = 1 << 30,  // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)
2094
};
2095
2096
// Flags for ImGui::TableNextRow()
2097
enum ImGuiTableRowFlags_
2098
{
2099
    ImGuiTableRowFlags_None                     = 0,
2100
    ImGuiTableRowFlags_Headers                  = 1 << 0,   // Identify header row (set default background color + width of its contents accounted differently for auto column width)
2101
};
2102
2103
// Enum for ImGui::TableSetBgColor()
2104
// Background colors are rendering in 3 layers:
2105
//  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
2106
//  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
2107
//  - Layer 2: draw with CellBg color if set.
2108
// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color.
2109
// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.
2110
// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
2111
// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
2112
enum ImGuiTableBgTarget_
2113
{
2114
    ImGuiTableBgTarget_None                     = 0,
2115
    ImGuiTableBgTarget_RowBg0                   = 1,        // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)
2116
    ImGuiTableBgTarget_RowBg1                   = 2,        // Set row background color 1 (generally used for selection marking)
2117
    ImGuiTableBgTarget_CellBg                   = 3,        // Set cell background color (top-most color)
2118
};
2119
2120
// Sorting specifications for a table (often handling sort specs for a single column, occasionally more)
2121
// Obtained by calling TableGetSortSpecs().
2122
// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.
2123
// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!
2124
struct ImGuiTableSortSpecs
2125
{
2126
    const ImGuiTableColumnSortSpecs* Specs;     // Pointer to sort spec array.
2127
    int                         SpecsCount;     // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled.
2128
    bool                        SpecsDirty;     // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.
2129
2130
0
    ImGuiTableSortSpecs()       { memset(this, 0, sizeof(*this)); }
2131
};
2132
2133
// Sorting specification for one column of a table (sizeof == 12 bytes)
2134
struct ImGuiTableColumnSortSpecs
2135
{
2136
    ImGuiID                     ColumnUserID;       // User id of the column (if specified by a TableSetupColumn() call)
2137
    ImS16                       ColumnIndex;        // Index of the column
2138
    ImS16                       SortOrder;          // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)
2139
    ImGuiSortDirection          SortDirection;      // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
2140
2141
0
    ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); }
2142
};
2143
2144
//-----------------------------------------------------------------------------
2145
// [SECTION] Helpers: Debug log, memory allocations macros, ImVector<>
2146
//-----------------------------------------------------------------------------
2147
2148
//-----------------------------------------------------------------------------
2149
// Debug Logging into ShowDebugLogWindow(), tty and more.
2150
//-----------------------------------------------------------------------------
2151
2152
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
2153
0
#define IMGUI_DEBUG_LOG(...)        ImGui::DebugLog(__VA_ARGS__)
2154
#else
2155
#define IMGUI_DEBUG_LOG(...)        ((void)0)
2156
#endif
2157
2158
//-----------------------------------------------------------------------------
2159
// IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE()
2160
// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax.
2161
// Defining a custom placement new() with a custom parameter allows us to bypass including <new> which on some platforms complains when user has disabled exceptions.
2162
//-----------------------------------------------------------------------------
2163
2164
struct ImNewWrapper {};
2165
146
inline void* operator new(size_t, ImNewWrapper, void* ptr) { return ptr; }
2166
0
inline void  operator delete(void*, ImNewWrapper, void*)   {} // This is only required so we can use the symmetrical new()
2167
231
#define IM_ALLOC(_SIZE)                     ImGui::MemAlloc(_SIZE)
2168
227
#define IM_FREE(_PTR)                       ImGui::MemFree(_PTR)
2169
266
#define IM_PLACEMENT_NEW(_PTR)              new(ImNewWrapper(), _PTR)
2170
24
#define IM_NEW(_TYPE)                       new(ImNewWrapper(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE
2171
17
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI12ImGuiContextEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI11ImFontAtlasEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI11ImGuiWindowEvPT_
Line
Count
Source
2171
3
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI14ImGuiViewportPEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
Unexecuted instantiation: _Z9IM_DELETEI10ImDrawListEvPT_
_Z9IM_DELETEIcEvPT_
Line
Count
Source
2171
3
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
Unexecuted instantiation: _Z9IM_DELETEI15ExampleTreeNodeEvPT_
_Z9IM_DELETEI33ImGui_ImplStbTrueType_FontSrcDataEvPT_
Line
Count
Source
2171
2
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI13ImTextureDataEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI6ImFontEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI18ImFontAtlasBuilderEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEIN5ImStb17STB_TexteditStateEEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI19ImGui_ImplSDL3_DataEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
_Z9IM_DELETEI27ImGui_ImplSDLRenderer3_DataEvPT_
Line
Count
Source
2171
1
template<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }
2172
2173
//-----------------------------------------------------------------------------
2174
// ImVector<>
2175
// Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug).
2176
//-----------------------------------------------------------------------------
2177
// - You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our public structures are relying on it.
2178
// - We use std-like naming convention here, which is a little unusual for this codebase.
2179
// - Important: clear() frees memory, resize(0) keep the allocated buffer. We use resize(0) a lot to intentionally recycle allocated buffers across frames and amortize our costs.
2180
// - Important: our implementation does NOT call C++ constructors/destructors, we treat everything as raw data! This is intentional but be extra mindful of that,
2181
//   Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset.
2182
//-----------------------------------------------------------------------------
2183
2184
IM_MSVC_RUNTIME_CHECKS_OFF
2185
template<typename T>
2186
struct ImVector
2187
{
2188
    int                 Size;
2189
    int                 Capacity;
2190
    T*                  Data;
2191
2192
    // Provide standard typedefs but we don't use them ourselves.
2193
    typedef T                   value_type;
2194
    typedef value_type*         iterator;
2195
    typedef const value_type*   const_iterator;
2196
2197
    // Constructors, destructor
2198
124
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeEC2Ev
_ZN8ImVectorI16ImGuiStoragePairEC2Ev
Line
Count
Source
2198
9
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI19ImGuiKeyRoutingDataEC2Ev
Line
Count
Source
2198
2
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI19ImGuiStackLevelInfoEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP10ImDrawListEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIcEC2Ev
Line
Count
Source
2198
13
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI13ImDrawChannelEC2Ev
Line
Count
Source
2198
4
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIjEC2Ev
Line
Count
Source
2198
4
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIhEC2Ev
Line
Count
Source
2198
5
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorItEC2Ev
Line
Count
Source
2198
6
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP13ImTextureDataEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEEC2Ev
_ZN8ImVectorIP11ImFontAtlasEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI15ImGuiInputEventEC2Ev
Line
Count
Source
2198
2
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP11ImGuiWindowEC2Ev
Line
Count
Source
2198
6
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI20ImGuiWindowStackDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI13ImGuiColorModEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI13ImGuiStyleModEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI15ImFontStackDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI19ImGuiFocusScopeDataEC2Ev
Line
Count
Source
2198
2
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIiEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI14ImGuiGroupDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI14ImGuiPopupDataEC2Ev
Line
Count
Source
2198
2
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI22ImGuiTreeNodeStackDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP14ImGuiViewportPEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI20ImGuiListClipperDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI18ImGuiTableTempDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI10ImGuiTableEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIfEC2Ev
Line
Count
Source
2198
9
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI11ImGuiTabBarEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI15ImGuiPtrOrIndexEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI20ImGuiShrinkWidthItemEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI24ImGuiMultiSelectTempDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI21ImGuiMultiSelectStateEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI20ImGuiSettingsHandlerEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI16ImGuiContextHookEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI15ImGuiOldColumnsEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeEC2Ev
Unexecuted instantiation: imgui_demo.cpp:_ZN8ImVectorIN12_GLOBAL__N_16MyItemEEC2Ev
Unexecuted instantiation: _ZN8ImVectorIPcEC2Ev
Unexecuted instantiation: _ZN8ImVectorIPKcEC2Ev
_ZN8ImVectorI6ImVec2EC2Ev
Line
Count
Source
2198
4
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentEC2Ev
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentEC2Ev
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetEC2Ev
_ZN8ImVectorI10stbrp_nodeEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI20ImFontAtlasRectEntryEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP11ImFontBakedEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI13ImTextureRectEC2Ev
Line
Count
Source
2198
2
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI9ImDrawCmdEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI10ImDrawVertEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI6ImVec4EC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI12ImTextureRefEC2Ev
Line
Count
Source
2198
3
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP6ImFontEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI12ImFontConfigEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP20ImDrawListSharedDataEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI11ImFontGlyphEC2Ev
Line
Count
Source
2198
2
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorIP12ImFontConfigEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
Unexecuted instantiation: _ZN8ImVectorI20ImGuiTableHeaderDataEC2Ev
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataEC2Ev
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTableInstanceDataEC2Ev
Unexecuted instantiation: _ZN8ImVectorI25ImGuiTableColumnSortSpecsEC2Ev
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestEC2Ev
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemEC2Ev
_ZN8ImVectorIP11SDL_GamepadEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
_ZN8ImVectorI10SDL_FColorEC2Ev
Line
Count
Source
2198
1
    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }
2199
0
    inline ImVector(const ImVector<T>& src)                 { Size = Capacity = 0; Data = NULL; operator=(src); }
Unexecuted instantiation: _ZN8ImVectorI20ImFontAtlasRectEntryEC2ERKS1_
Unexecuted instantiation: _ZN8ImVectorItEC2ERKS0_
2200
0
    inline ImVector<T>& operator=(const ImVector<T>& src)   { clear(); resize(src.Size); if (Data && src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; }
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdEaSERKS1_
Unexecuted instantiation: _ZN8ImVectorItEaSERKS0_
Unexecuted instantiation: _ZN8ImVectorI10ImDrawVertEaSERKS1_
Unexecuted instantiation: _ZN8ImVectorI20ImFontAtlasRectEntryEaSERKS1_
2201
124
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeED2Ev
_ZN8ImVectorI19ImGuiKeyRoutingDataED2Ev
Line
Count
Source
2201
2
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI19ImGuiStackLevelInfoED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI11ImFontGlyphED2Ev
Line
Count
Source
2201
2
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI16ImGuiStoragePairED2Ev
Line
Count
Source
2201
9
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP10ImDrawListED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIcED2Ev
Line
Count
Source
2201
13
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI13ImDrawChannelED2Ev
Line
Count
Source
2201
4
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIjED2Ev
Line
Count
Source
2201
4
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIhED2Ev
Line
Count
Source
2201
5
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorItED2Ev
Line
Count
Source
2201
6
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP13ImTextureDataED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEED2Ev
_ZN8ImVectorI10ImGuiTableED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI11ImGuiTabBarED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI21ImGuiMultiSelectStateED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIiED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP11ImFontAtlasED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI15ImGuiInputEventED2Ev
Line
Count
Source
2201
2
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP11ImGuiWindowED2Ev
Line
Count
Source
2201
6
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI20ImGuiWindowStackDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI13ImGuiColorModED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI13ImGuiStyleModED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI15ImFontStackDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI19ImGuiFocusScopeDataED2Ev
Line
Count
Source
2201
2
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI14ImGuiGroupDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI14ImGuiPopupDataED2Ev
Line
Count
Source
2201
2
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI22ImGuiTreeNodeStackDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP14ImGuiViewportPED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI20ImGuiListClipperDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI18ImGuiTableTempDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIfED2Ev
Line
Count
Source
2201
9
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI15ImGuiPtrOrIndexED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI20ImGuiShrinkWidthItemED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI24ImGuiMultiSelectTempDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI20ImGuiSettingsHandlerED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI16ImGuiContextHookED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemED2Ev
Unexecuted instantiation: _ZN8ImVectorI25ImGuiTableColumnSortSpecsED2Ev
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTableInstanceDataED2Ev
Unexecuted instantiation: _ZN8ImVectorI20ImGuiTableHeaderDataED2Ev
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestED2Ev
_ZN8ImVectorI15ImGuiOldColumnsED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataED2Ev
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeED2Ev
Unexecuted instantiation: imgui_demo.cpp:_ZN8ImVectorIN12_GLOBAL__N_16MyItemEED2Ev
Unexecuted instantiation: _ZN8ImVectorIPcED2Ev
Unexecuted instantiation: _ZN8ImVectorIPKcED2Ev
_ZN8ImVectorI6ImVec2ED2Ev
Line
Count
Source
2201
4
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentED2Ev
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentED2Ev
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetED2Ev
_ZN8ImVectorIP11ImFontBakedED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI10stbrp_nodeED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI13ImTextureRectED2Ev
Line
Count
Source
2201
2
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI20ImFontAtlasRectEntryED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI9ImDrawCmdED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI10ImDrawVertED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI6ImVec4ED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI12ImTextureRefED2Ev
Line
Count
Source
2201
3
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP6ImFontED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI12ImFontConfigED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP20ImDrawListSharedDataED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP12ImFontConfigED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorIP11SDL_GamepadED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
_ZN8ImVectorI10SDL_FColorED2Ev
Line
Count
Source
2201
1
    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything
2202
2203
95
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIcE5clearEv
Line
Count
Source
2203
7
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI16ImGuiStoragePairE5clearEv
Line
Count
Source
2203
7
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI19ImGuiKeyRoutingDataE5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIjE5clearEv
Line
Count
Source
2203
2
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIiE5clearEv
Line
Count
Source
2203
2
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI15ImGuiInputEventE5clearEv
_ZN8ImVectorI6ImVec2E5clearEv
Line
Count
Source
2203
5
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIP11ImGuiWindowE5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI20ImGuiWindowStackDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI13ImGuiColorModE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI13ImGuiStyleModE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI15ImFontStackDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI14ImGuiPopupDataE5clearEv
Line
Count
Source
2203
2
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI22ImGuiTreeNodeStackDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIP14ImGuiViewportPE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI11ImGuiTabBarE5clearEv
Line
Count
Source
2203
2
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI15ImGuiPtrOrIndexE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI20ImGuiShrinkWidthItemE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI20ImGuiListClipperDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI10ImGuiTableE5clearEv
Line
Count
Source
2203
2
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI18ImGuiTableTempDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI13ImDrawChannelE5clearEv
Line
Count
Source
2203
8
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI21ImGuiMultiSelectStateE5clearEv
Line
Count
Source
2203
2
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI24ImGuiMultiSelectTempDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI20ImGuiSettingsHandlerE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI15ImGuiOldColumnsE5clearEv
Line
Count
Source
2203
3
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI14ImGuiGroupDataE5clearEv
_ZN8ImVectorIfE5clearEv
Line
Count
Source
2203
3
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIhE5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorIPcE5clearEv
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentE5clearEv
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetE5clearEv
_ZN8ImVectorI9ImDrawCmdE5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorItE5clearEv
Line
Count
Source
2203
5
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI10ImDrawVertE5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI6ImVec4E5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI12ImTextureRefE5clearEv
Line
Count
Source
2203
4
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIP13ImTextureDataE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIP12ImFontConfigE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorI12ImFontConfigE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
_ZN8ImVectorIP6ImFontE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI20ImFontAtlasRectEntryE5clearEv
_ZN8ImVectorI11ImFontGlyphE5clearEv
Line
Count
Source
2203
1
    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything
Unexecuted instantiation: _ZN8ImVectorI25ImGuiTableColumnSortSpecsE5clearEv
2204
10
    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.
_ZN8ImVectorIP11ImGuiWindowE12clear_deleteEv
Line
Count
Source
2204
4
    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.
_ZN8ImVectorIP14ImGuiViewportPE12clear_deleteEv
Line
Count
Source
2204
2
    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.
_ZN8ImVectorIP13ImTextureDataE12clear_deleteEv
Line
Count
Source
2204
2
    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.
_ZN8ImVectorIP6ImFontE12clear_deleteEv
Line
Count
Source
2204
2
    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.
2205
6
    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.
_ZN8ImVectorI20ImGuiListClipperDataE14clear_destructEv
Line
Count
Source
2205
1
    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.
_ZN8ImVectorI18ImGuiTableTempDataE14clear_destructEv
Line
Count
Source
2205
1
    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.
_ZN8ImVectorI24ImGuiMultiSelectTempDataE14clear_destructEv
Line
Count
Source
2205
1
    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.
_ZN8ImVectorI15ImGuiOldColumnsE14clear_destructEv
Line
Count
Source
2205
3
    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.
2206
2207
242k
    inline bool         empty() const                       { return Size == 0; }
Unexecuted instantiation: _ZNK8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE5emptyEv
_ZNK8ImVectorIP10ImDrawListE5emptyEv
Line
Count
Source
2207
80.5k
    inline bool         empty() const                       { return Size == 0; }
Unexecuted instantiation: _ZNK8ImVectorIcE5emptyEv
_ZNK8ImVectorI20ImGuiWindowStackDataE5emptyEv
Line
Count
Source
2207
161k
    inline bool         empty() const                       { return Size == 0; }
Unexecuted instantiation: _ZNK8ImVectorIP10MyDocumentE5emptyEv
Unexecuted instantiation: _ZNK8ImVectorItE5emptyEv
Unexecuted instantiation: _ZNK8ImVectorIP6ImFontE5emptyEv
Unexecuted instantiation: _ZNK8ImVectorI15ImGuiPtrOrIndexE5emptyEv
2208
0
    inline int          size() const                        { return Size; }
Unexecuted instantiation: _ZNK8ImVectorIcE4sizeEv
Unexecuted instantiation: _ZNK8ImVectorI6ImVec2E4sizeEv
2209
0
    inline int          size_in_bytes() const               { return Size * (int)sizeof(T); }
Unexecuted instantiation: _ZNK8ImVectorI16ImGuiStoragePairE13size_in_bytesEv
Unexecuted instantiation: _ZNK8ImVectorIiE13size_in_bytesEv
Unexecuted instantiation: _ZNK8ImVectorIjE13size_in_bytesEv
2210
    inline int          max_size() const                    { return 0x7FFFFFFF / (int)sizeof(T); }
2211
0
    inline int          capacity() const                    { return Capacity; }
2212
1.62M
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorIjEixEi
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeEixEi
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTableInstanceDataEixEi
Unexecuted instantiation: _ZN8ImVectorIiEixEi
_ZN8ImVectorIcEixEi
Line
Count
Source
2212
223
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorI15ImGuiInputEventEixEi
Line
Count
Source
2212
1.87k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorI16ImGuiStoragePairEixEi
Unexecuted instantiation: _ZN8ImVectorI20ImGuiListClipperDataEixEi
Unexecuted instantiation: _ZN8ImVectorI11ImGuiTabBarEixEi
Unexecuted instantiation: _ZN8ImVectorI10ImGuiTableEixEi
Unexecuted instantiation: _ZN8ImVectorI21ImGuiMultiSelectStateEixEi
_ZN8ImVectorIP14ImGuiViewportPEixEi
Line
Count
Source
2212
648k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorI16ImGuiContextHookEixEi
_ZN8ImVectorIfEixEi
Line
Count
Source
2212
22
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorIP11ImGuiWindowEixEi
Line
Count
Source
2212
238k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorI14ImGuiPopupDataEixEi
Line
Count
Source
2212
5.93k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorI9ImDrawCmdEixEi
Line
Count
Source
2212
486k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorIP6ImFontEixEi
Line
Count
Source
2212
80.5k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorI19ImGuiKeyRoutingDataEixEi
_ZN8ImVectorI20ImGuiWindowStackDataEixEi
Line
Count
Source
2212
81.2k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorIP13ImTextureDataEixEi
Line
Count
Source
2212
80.5k
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorItEixEi
Line
Count
Source
2212
22
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorI19ImGuiFocusScopeDataEixEi
_ZN8ImVectorIP12ImFontConfigEixEi
Line
Count
Source
2212
44
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorIP11ImFontBakedEixEi
Line
Count
Source
2212
3
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemEixEi
Unexecuted instantiation: _ZN8ImVectorI19ImGuiStackLevelInfoEixEi
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeEixEi
Unexecuted instantiation: imgui_demo.cpp:_ZN8ImVectorIN12_GLOBAL__N_16MyItemEEixEi
Unexecuted instantiation: _ZN8ImVectorIPcEixEi
Unexecuted instantiation: _ZN8ImVectorIPKcEixEi
Unexecuted instantiation: _ZN8ImVectorI6ImVec2EixEi
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentEixEi
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentEixEi
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetEixEi
Unexecuted instantiation: _ZN8ImVectorI13ImDrawChannelEixEi
Unexecuted instantiation: _ZN8ImVectorI10ImDrawVertEixEi
Unexecuted instantiation: _ZN8ImVectorI12ImFontConfigEixEi
_ZN8ImVectorI13ImTextureRectEixEi
Line
Count
Source
2212
64
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorI20ImFontAtlasRectEntryEixEi
Line
Count
Source
2212
106
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
_ZN8ImVectorI11ImFontGlyphEixEi
Line
Count
Source
2212
22
    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZN8ImVectorI18ImGuiTableTempDataEixEi
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataEixEi
Unexecuted instantiation: _ZN8ImVectorI15ImGuiOldColumnsEixEi
Unexecuted instantiation: _ZN8ImVectorI20ImGuiShrinkWidthItemEixEi
Unexecuted instantiation: _ZN8ImVectorI24ImGuiMultiSelectTempDataEixEi
2213
162k
    inline const T&     operator[](int i) const             { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
Unexecuted instantiation: _ZNK8ImVectorIjEixEi
Unexecuted instantiation: _ZNK8ImVectorIP12ImFontConfigEixEi
Unexecuted instantiation: _ZNK8ImVectorIcEixEi
_ZNK8ImVectorI9ImDrawCmdEixEi
Line
Count
Source
2213
162k
    inline const T&     operator[](int i) const             { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }
2214
2215
2.33M
    inline T*           begin()                             { return Data; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE5beginEv
Unexecuted instantiation: _ZN8ImVectorIcE5beginEv
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE5beginEv
_ZN8ImVectorIP14ImGuiViewportPE5beginEv
Line
Count
Source
2215
402k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP11ImFontAtlasE5beginEv
Line
Count
Source
2215
322k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorI16ImGuiContextHookE5beginEv
Line
Count
Source
2215
483k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP11ImGuiWindowE5beginEv
Line
Count
Source
2215
323k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorI18ImGuiTableTempDataE5beginEv
Line
Count
Source
2215
80.5k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP10ImDrawListE5beginEv
Line
Count
Source
2215
161k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP13ImTextureDataE5beginEv
Line
Count
Source
2215
322k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorI14ImGuiPopupDataE5beginEv
Line
Count
Source
2215
1
    inline T*           begin()                             { return Data; }
_ZN8ImVectorI20ImGuiSettingsHandlerE5beginEv
Line
Count
Source
2215
34
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP6ImFontE5beginEv
Line
Count
Source
2215
80.5k
    inline T*           begin()                             { return Data; }
Unexecuted instantiation: _ZN8ImVectorI20ImFontAtlasRectEntryE5beginEv
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataE5beginEv
Unexecuted instantiation: _ZN8ImVectorI16ImGuiStoragePairE5beginEv
Unexecuted instantiation: _ZN8ImVectorI15ImGuiOldColumnsE5beginEv
_ZN8ImVectorIP11ImFontBakedE5beginEv
Line
Count
Source
2215
1
    inline T*           begin()                             { return Data; }
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeE5beginEv
Unexecuted instantiation: _ZN8ImVectorIjE5beginEv
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestE5beginEv
Unexecuted instantiation: _ZN8ImVectorIPcE5beginEv
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentE5beginEv
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentE5beginEv
Unexecuted instantiation: _ZN8ImVectorI15ImFontStackDataE5beginEv
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdE5beginEv
_ZN8ImVectorI12ImFontConfigE5beginEv
Line
Count
Source
2215
3
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP20ImDrawListSharedDataE5beginEv
Line
Count
Source
2215
161k
    inline T*           begin()                             { return Data; }
_ZN8ImVectorIP12ImFontConfigE5beginEv
Line
Count
Source
2215
30
    inline T*           begin()                             { return Data; }
_ZN8ImVectorI11ImFontGlyphE5beginEv
Line
Count
Source
2215
1
    inline T*           begin()                             { return Data; }
Unexecuted instantiation: _ZN8ImVectorI12ImTextureRefE5beginEv
_ZN8ImVectorI13ImTextureRectE5beginEv
Line
Count
Source
2215
2
    inline T*           begin()                             { return Data; }
Unexecuted instantiation: _ZN8ImVectorIiE5beginEv
Unexecuted instantiation: _ZN8ImVectorItE5beginEv
_ZN8ImVectorIP11SDL_GamepadE5beginEv
Line
Count
Source
2215
2
    inline T*           begin()                             { return Data; }
2216
0
    inline const T*     begin() const                       { return Data; }
Unexecuted instantiation: _ZNK8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE5beginEv
Unexecuted instantiation: _ZNK8ImVectorI9ImDrawCmdE5beginEv
Unexecuted instantiation: _ZNK8ImVectorI21ImGuiSelectionRequestE5beginEv
2217
2.33M
    inline T*           end()                               { return Data + Size; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE3endEv
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE3endEv
_ZN8ImVectorIP14ImGuiViewportPE3endEv
Line
Count
Source
2217
402k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP11ImFontAtlasE3endEv
Line
Count
Source
2217
322k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorI16ImGuiContextHookE3endEv
Line
Count
Source
2217
483k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP11ImGuiWindowE3endEv
Line
Count
Source
2217
323k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorI18ImGuiTableTempDataE3endEv
Line
Count
Source
2217
80.5k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP10ImDrawListE3endEv
Line
Count
Source
2217
161k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP13ImTextureDataE3endEv
Line
Count
Source
2217
322k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorI14ImGuiPopupDataE3endEv
Line
Count
Source
2217
1
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorI20ImGuiSettingsHandlerE3endEv
Line
Count
Source
2217
34
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP6ImFontE3endEv
Line
Count
Source
2217
80.5k
    inline T*           end()                               { return Data + Size; }
Unexecuted instantiation: _ZN8ImVectorI20ImFontAtlasRectEntryE3endEv
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataE3endEv
Unexecuted instantiation: _ZN8ImVectorI16ImGuiStoragePairE3endEv
Unexecuted instantiation: _ZN8ImVectorI15ImGuiOldColumnsE3endEv
_ZN8ImVectorIP11ImFontBakedE3endEv
Line
Count
Source
2217
1
    inline T*           end()                               { return Data + Size; }
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeE3endEv
Unexecuted instantiation: _ZN8ImVectorIjE3endEv
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestE3endEv
Unexecuted instantiation: _ZN8ImVectorIPcE3endEv
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentE3endEv
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentE3endEv
Unexecuted instantiation: _ZN8ImVectorI15ImFontStackDataE3endEv
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdE3endEv
_ZN8ImVectorI12ImFontConfigE3endEv
Line
Count
Source
2217
3
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP20ImDrawListSharedDataE3endEv
Line
Count
Source
2217
161k
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorIP12ImFontConfigE3endEv
Line
Count
Source
2217
30
    inline T*           end()                               { return Data + Size; }
_ZN8ImVectorI11ImFontGlyphE3endEv
Line
Count
Source
2217
1
    inline T*           end()                               { return Data + Size; }
Unexecuted instantiation: _ZN8ImVectorI12ImTextureRefE3endEv
_ZN8ImVectorI13ImTextureRectE3endEv
Line
Count
Source
2217
2
    inline T*           end()                               { return Data + Size; }
Unexecuted instantiation: _ZN8ImVectorIiE3endEv
Unexecuted instantiation: _ZN8ImVectorItE3endEv
_ZN8ImVectorIP11SDL_GamepadE3endEv
Line
Count
Source
2217
2
    inline T*           end()                               { return Data + Size; }
2218
0
    inline const T*     end() const                         { return Data + Size; }
Unexecuted instantiation: _ZNK8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE3endEv
Unexecuted instantiation: _ZNK8ImVectorI21ImGuiSelectionRequestE3endEv
2219
    inline T&           front()                             { IM_ASSERT(Size > 0); return Data[0]; }
2220
0
    inline const T&     front() const                       { IM_ASSERT(Size > 0); return Data[0]; }
2221
3.15M
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
Unexecuted instantiation: _ZN8ImVectorI13ImGuiColorModE4backEv
_ZN8ImVectorI13ImGuiStyleModE4backEv
Line
Count
Source
2221
403k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
Unexecuted instantiation: _ZN8ImVectorI16ImGuiContextHookE4backEv
_ZN8ImVectorI20ImGuiWindowStackDataE4backEv
Line
Count
Source
2221
729k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
_ZN8ImVectorIiE4backEv
Line
Count
Source
2221
84.5k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
_ZN8ImVectorI6ImVec4E4backEv
Line
Count
Source
2221
646k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdE4backEv
Unexecuted instantiation: _ZN8ImVectorI14ImGuiPopupDataE4backEv
Unexecuted instantiation: _ZN8ImVectorIfE4backEv
_ZN8ImVectorI19ImGuiFocusScopeDataE4backEv
Line
Count
Source
2221
81.2k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
_ZN8ImVectorI15ImFontStackDataE4backEv
Line
Count
Source
2221
80.5k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
_ZN8ImVectorIjE4backEv
Line
Count
Source
2221
890k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
_ZN8ImVectorI14ImGuiGroupDataE4backEv
Line
Count
Source
2221
241k
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
_ZN8ImVectorIP11ImGuiWindowE4backEv
Line
Count
Source
2221
14
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
Unexecuted instantiation: _ZN8ImVectorI6ImVec2E4backEv
Unexecuted instantiation: _ZN8ImVectorI12ImTextureRefE4backEv
Unexecuted instantiation: _ZN8ImVectorIP6ImFontE4backEv
_ZN8ImVectorI12ImFontConfigE4backEv
Line
Count
Source
2221
1
    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }
Unexecuted instantiation: _ZN8ImVectorI15ImGuiOldColumnsE4backEv
Unexecuted instantiation: _ZN8ImVectorI15ImGuiPtrOrIndexE4backEv
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemE4backEv
2222
0
    inline const T&     back() const                        { IM_ASSERT(Size > 0); return Data[Size - 1]; }
Unexecuted instantiation: _ZNK8ImVectorIcE4backEv
Unexecuted instantiation: _ZNK8ImVectorI6ImVec4E4backEv
Unexecuted instantiation: _ZNK8ImVectorI9ImDrawCmdE4backEv
2223
161k
    inline void         swap(ImVector<T>& rhs)              { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }
_ZN8ImVectorI19ImGuiKeyRoutingDataE4swapERS1_
Line
Count
Source
2223
80.5k
    inline void         swap(ImVector<T>& rhs)              { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }
_ZN8ImVectorIP11ImGuiWindowE4swapERS2_
Line
Count
Source
2223
80.5k
    inline void         swap(ImVector<T>& rhs)              { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }
Unexecuted instantiation: _ZN8ImVectorIjE4swapERS0_
Unexecuted instantiation: _ZN8ImVectorIiE4swapERS0_
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetE4swapERS1_
Unexecuted instantiation: _ZN8ImVectorI10ImDrawVertE4swapERS1_
Unexecuted instantiation: _ZN8ImVectorI13ImTextureRectE4swapERS1_
Unexecuted instantiation: _ZN8ImVectorIcE4swapERS0_
Unexecuted instantiation: _ZN8ImVectorItE4swapERS0_
Unexecuted instantiation: _ZN8ImVectorIfE4swapERS0_
Unexecuted instantiation: _ZN8ImVectorI16ImGuiStoragePairE4swapERS1_
2224
2225
115
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIcE14_grow_capacityEi
Line
Count
Source
2225
14
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI6ImVec2E14_grow_capacityEi
Line
Count
Source
2225
3
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIjE14_grow_capacityEi
Line
Count
Source
2225
4
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI21ImGuiListClipperRangeE14_grow_capacityEi
_ZNK8ImVectorIP10ImDrawListE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI9ImDrawCmdE14_grow_capacityEi
Line
Count
Source
2225
3
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP11ImGuiWindowE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP13ImTextureDataE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI19ImGuiKeyRoutingDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI19ImGuiStackLevelInfoE14_grow_capacityEi
_ZNK8ImVectorI15ImGuiInputEventE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorItE14_grow_capacityEi
Line
Count
Source
2225
21
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI16ImGuiStoragePairE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE14_grow_capacityEi
_ZNK8ImVectorIiE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI20ImGuiListClipperDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI13ImGuiColorModE14_grow_capacityEi
_ZNK8ImVectorI13ImGuiStyleModE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP14ImGuiViewportPE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI16ImGuiContextHookE14_grow_capacityEi
_ZNK8ImVectorI20ImGuiWindowStackDataE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI14ImGuiPopupDataE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI14ImGuiGroupDataE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIfE14_grow_capacityEi
Line
Count
Source
2225
4
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI19ImGuiFocusScopeDataE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI15ImFontStackDataE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP11ImFontAtlasE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIhE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI20ImGuiSettingsHandlerE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI21ImGuiSelectionRequestE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorIP15ExampleTreeNodeE14_grow_capacityEi
Unexecuted instantiation: imgui_demo.cpp:_ZNK8ImVectorIN12_GLOBAL__N_16MyItemEE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorIPKcE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorIPcE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI10MyDocumentE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorIP10MyDocumentE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI12ExampleAssetE14_grow_capacityEi
_ZNK8ImVectorI20ImFontAtlasRectEntryE14_grow_capacityEi
Line
Count
Source
2225
4
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI10ImDrawVertE14_grow_capacityEi
Line
Count
Source
2225
15
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI6ImVec4E14_grow_capacityEi
Line
Count
Source
2225
3
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI12ImTextureRefE14_grow_capacityEi
Line
Count
Source
2225
3
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI13ImDrawChannelE14_grow_capacityEi
_ZNK8ImVectorI13ImTextureRectE14_grow_capacityEi
Line
Count
Source
2225
5
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP6ImFontE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI12ImFontConfigE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP12ImFontConfigE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP11ImFontBakedE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorIP20ImDrawListSharedDataE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI10stbrp_nodeE14_grow_capacityEi
Line
Count
Source
2225
1
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
_ZNK8ImVectorI11ImFontGlyphE14_grow_capacityEi
Line
Count
Source
2225
4
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
Unexecuted instantiation: _ZNK8ImVectorI10ImGuiTableE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI18ImGuiTableTempDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI22ImGuiTableInstanceDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI25ImGuiTableColumnSortSpecsE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI20ImGuiTableHeaderDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI15ImGuiOldColumnsE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI18ImGuiOldColumnDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI22ImGuiTreeNodeStackDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI12ImGuiTabItemE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI20ImGuiShrinkWidthItemE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI24ImGuiMultiSelectTempDataE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI21ImGuiMultiSelectStateE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI11ImGuiTabBarE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorI15ImGuiPtrOrIndexE14_grow_capacityEi
Unexecuted instantiation: _ZNK8ImVectorIP11SDL_GamepadE14_grow_capacityEi
_ZNK8ImVectorI10SDL_FColorE14_grow_capacityEi
Line
Count
Source
2225
2
    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }
2226
5.17M
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIjE6resizeEi
Line
Count
Source
2226
242k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE6resizeEi
_ZN8ImVectorIP10ImDrawListE6resizeEi
Line
Count
Source
2226
241k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIP13ImTextureDataE6resizeEi
Line
Count
Source
2226
80.5k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI19ImGuiKeyRoutingDataE6resizeEi
Line
Count
Source
2226
80.5k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI19ImGuiStackLevelInfoE6resizeEi
_ZN8ImVectorItE6resizeEi
Line
Count
Source
2226
1.13M
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE6resizeEi
_ZN8ImVectorIcE6resizeEi
Line
Count
Source
2226
199
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI15ImGuiInputEventE6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI20ImGuiWindowStackDataE6resizeEi
Line
Count
Source
2226
242k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI14ImGuiPopupDataE6resizeEi
Line
Count
Source
2226
80.5k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIiE6resizeEi
Line
Count
Source
2226
80.5k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI14ImGuiGroupDataE6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIP11ImGuiWindowE6resizeEi
Line
Count
Source
2226
242k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIfE6resizeEi
Line
Count
Source
2226
323k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI19ImGuiFocusScopeDataE6resizeEi
Line
Count
Source
2226
1.07k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIhE6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestE6resizeEi
_ZN8ImVectorI6ImVec2E6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI20ImFontAtlasRectEntryE6resizeEi
Line
Count
Source
2226
22
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI9ImDrawCmdE6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI10ImDrawVertE6resizeEi
Line
Count
Source
2226
1.05M
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI6ImVec4E6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI12ImTextureRefE6resizeEi
Line
Count
Source
2226
161k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI13ImDrawChannelE6resizeEi
_ZN8ImVectorI13ImTextureRectE6resizeEi
Line
Count
Source
2226
80.5k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIP12ImFontConfigE6resizeEi
Line
Count
Source
2226
1
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorIP11ImFontBakedE6resizeEi
Line
Count
Source
2226
1
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI10stbrp_nodeE6resizeEi
Line
Count
Source
2226
1
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI10ImGuiTableE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI25ImGuiTableColumnSortSpecsE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI20ImGuiTableHeaderDataE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTreeNodeStackDataE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI20ImGuiShrinkWidthItemE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI21ImGuiMultiSelectStateE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI16ImGuiStoragePairE6resizeEi
Unexecuted instantiation: _ZN8ImVectorI11ImGuiTabBarE6resizeEi
_ZN8ImVectorIP11SDL_GamepadE6resizeEi
Line
Count
Source
2226
2
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
_ZN8ImVectorI10SDL_FColorE6resizeEi
Line
Count
Source
2226
162k
    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
2227
3.33k
    inline void         resize(int new_size, const T& v)    { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }
_ZN8ImVectorIcE6resizeEiRKc
Line
Count
Source
2227
3.07k
    inline void         resize(int new_size, const T& v)    { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI20ImGuiListClipperDataE6resizeEiRKS0_
Unexecuted instantiation: _ZN8ImVectorI19ImGuiStackLevelInfoE6resizeEiRKS0_
Unexecuted instantiation: imgui_demo.cpp:_ZN8ImVectorIN12_GLOBAL__N_16MyItemEE6resizeEiRKS1_
_ZN8ImVectorIfE6resizeEiRKf
Line
Count
Source
2227
128
    inline void         resize(int new_size, const T& v)    { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }
_ZN8ImVectorItE6resizeEiRKt
Line
Count
Source
2227
128
    inline void         resize(int new_size, const T& v)    { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }
Unexecuted instantiation: _ZN8ImVectorI18ImGuiTableTempDataE6resizeEiRKS0_
Unexecuted instantiation: _ZN8ImVectorI24ImGuiMultiSelectTempDataE6resizeEiRKS0_
2228
0
    inline void         shrink(int new_size)                { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation
Unexecuted instantiation: _ZN8ImVectorI10ImDrawVertE6shrinkEi
Unexecuted instantiation: _ZN8ImVectorItE6shrinkEi
2229
80.7k
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIcE7reserveEi
Line
Count
Source
2229
41
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI6ImVec2E7reserveEi
Line
Count
Source
2229
3
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIjE7reserveEi
Line
Count
Source
2229
4
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE7reserveEi
_ZN8ImVectorIP10ImDrawListE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI9ImDrawCmdE7reserveEi
Line
Count
Source
2229
3
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP13ImTextureDataE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI19ImGuiKeyRoutingDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI19ImGuiStackLevelInfoE7reserveEi
_ZN8ImVectorI15ImGuiInputEventE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI16ImGuiStoragePairE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE7reserveEi
_ZN8ImVectorIiE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI20ImGuiListClipperDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI13ImGuiColorModE7reserveEi
_ZN8ImVectorI13ImGuiStyleModE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP14ImGuiViewportPE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI16ImGuiContextHookE7reserveEi
_ZN8ImVectorItE7reserveEi
Line
Count
Source
2229
21
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI10ImDrawVertE7reserveEi
Line
Count
Source
2229
15
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI20ImGuiWindowStackDataE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI14ImGuiPopupDataE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI14ImGuiGroupDataE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP11ImGuiWindowE7reserveEi
Line
Count
Source
2229
80.5k
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIfE7reserveEi
Line
Count
Source
2229
4
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI19ImGuiFocusScopeDataE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI15ImFontStackDataE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP11ImFontAtlasE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIhE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI20ImGuiSettingsHandlerE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestE7reserveEi
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeE7reserveEi
Unexecuted instantiation: imgui_demo.cpp:_ZN8ImVectorIN12_GLOBAL__N_16MyItemEE7reserveEi
Unexecuted instantiation: _ZN8ImVectorIPKcE7reserveEi
Unexecuted instantiation: _ZN8ImVectorIPcE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentE7reserveEi
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetE7reserveEi
_ZN8ImVectorI20ImFontAtlasRectEntryE7reserveEi
Line
Count
Source
2229
4
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI6ImVec4E7reserveEi
Line
Count
Source
2229
3
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI12ImTextureRefE7reserveEi
Line
Count
Source
2229
3
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI13ImDrawChannelE7reserveEi
_ZN8ImVectorI13ImTextureRectE7reserveEi
Line
Count
Source
2229
5
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP6ImFontE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI12ImFontConfigE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP12ImFontConfigE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP11ImFontBakedE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorIP20ImDrawListSharedDataE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI10stbrp_nodeE7reserveEi
Line
Count
Source
2229
1
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
_ZN8ImVectorI11ImFontGlyphE7reserveEi
Line
Count
Source
2229
4
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
Unexecuted instantiation: _ZN8ImVectorI10ImGuiTableE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI18ImGuiTableTempDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTableInstanceDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI25ImGuiTableColumnSortSpecsE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI20ImGuiTableHeaderDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI15ImGuiOldColumnsE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTreeNodeStackDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI20ImGuiShrinkWidthItemE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI24ImGuiMultiSelectTempDataE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI21ImGuiMultiSelectStateE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI11ImGuiTabBarE7reserveEi
Unexecuted instantiation: _ZN8ImVectorI15ImGuiPtrOrIndexE7reserveEi
Unexecuted instantiation: _ZN8ImVectorIP11SDL_GamepadE7reserveEi
_ZN8ImVectorI10SDL_FColorE7reserveEi
Line
Count
Source
2229
2
    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }
2230
243k
    inline void         reserve_discard(int new_capacity)   { if (new_capacity <= Capacity) return; if (Data) IM_FREE(Data); Data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); Capacity = new_capacity; }
2231
2232
    // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden.
2233
3.56M
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI6ImVec2E9push_backERKS0_
Line
Count
Source
2233
891k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI9ImDrawCmdE9push_backERKS0_
Line
Count
Source
2233
404k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI15ImGuiInputEventE9push_backERKS0_
Line
Count
Source
2233
1.39k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorIN15ImGuiTextFilter14ImGuiTextRangeEE9push_backERKS1_
_ZN8ImVectorIiE9push_backERKi
Line
Count
Source
2233
82.5k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI13ImGuiColorModE9push_backERKS0_
_ZN8ImVectorI13ImGuiStyleModE9push_backERKS0_
Line
Count
Source
2233
403k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP14ImGuiViewportPE9push_backERKS1_
Line
Count
Source
2233
1
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorI16ImGuiContextHookE9push_backERKS0_
_ZN8ImVectorIjE9push_backERKj
Line
Count
Source
2233
566k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP11ImGuiWindowE9push_backERKS1_
Line
Count
Source
2233
240k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI14ImGuiPopupDataE9push_backERKS0_
Line
Count
Source
2233
661
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorIfE9push_backERKf
_ZN8ImVectorI19ImGuiFocusScopeDataE9push_backERKS0_
Line
Count
Source
2233
162k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP13ImTextureDataE9push_backERKS1_
Line
Count
Source
2233
80.5k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI15ImFontStackDataE9push_backERKS0_
Line
Count
Source
2233
80.5k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP11ImFontAtlasE9push_backERKS1_
Line
Count
Source
2233
1
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorI19ImGuiKeyRoutingDataE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorItE9push_backERKt
_ZN8ImVectorI20ImGuiSettingsHandlerE9push_backERKS0_
Line
Count
Source
2233
2
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIcE9push_backERKc
Line
Count
Source
2233
1
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorIP15ExampleTreeNodeE9push_backERKS1_
Unexecuted instantiation: _ZN8ImVectorIPKcE9push_backERKS1_
Unexecuted instantiation: _ZN8ImVectorIPcE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI10MyDocumentE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorIP10MyDocumentE9push_backERKS1_
Unexecuted instantiation: _ZN8ImVectorI12ExampleAssetE9push_backERKS0_
_ZN8ImVectorIP10ImDrawListE9push_backERKS1_
Line
Count
Source
2233
81.2k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI6ImVec4E9push_backERKS0_
Line
Count
Source
2233
404k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI12ImTextureRefE9push_backERKS0_
Line
Count
Source
2233
161k
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI13ImTextureRectE9push_backERKS0_
Line
Count
Source
2233
26
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP6ImFontE9push_backERKS1_
Line
Count
Source
2233
1
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI12ImFontConfigE9push_backERKS0_
Line
Count
Source
2233
1
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP12ImFontConfigE9push_backERKS1_
Line
Count
Source
2233
2
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorIP20ImDrawListSharedDataE9push_backERKS1_
Line
Count
Source
2233
1
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
_ZN8ImVectorI11ImFontGlyphE9push_backERKS0_
Line
Count
Source
2233
22
    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTableInstanceDataE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI20ImGuiTableHeaderDataE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI15ImGuiOldColumnsE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI18ImGuiOldColumnDataE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI16ImGuiStoragePairE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI21ImGuiSelectionRequestE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI15ImGuiPtrOrIndexE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemE9push_backERKS0_
Unexecuted instantiation: _ZN8ImVectorIP11SDL_GamepadE9push_backERKS1_
2234
1.61M
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorI9ImDrawCmdE8pop_backEv
Line
Count
Source
2234
161k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
Unexecuted instantiation: _ZN8ImVectorI13ImGuiColorModE8pop_backEv
_ZN8ImVectorI13ImGuiStyleModE8pop_backEv
Line
Count
Source
2234
403k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorI14ImGuiPopupDataE8pop_backEv
Line
Count
Source
2234
660
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorI20ImGuiWindowStackDataE8pop_backEv
Line
Count
Source
2234
161k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorIiE8pop_backEv
Line
Count
Source
2234
1.97k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
Unexecuted instantiation: _ZN8ImVectorIfE8pop_backEv
_ZN8ImVectorI19ImGuiFocusScopeDataE8pop_backEv
Line
Count
Source
2234
161k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorI15ImFontStackDataE8pop_backEv
Line
Count
Source
2234
80.5k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorIjE8pop_backEv
Line
Count
Source
2234
324k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorI14ImGuiGroupDataE8pop_backEv
Line
Count
Source
2234
80.5k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
_ZN8ImVectorI6ImVec4E8pop_backEv
Line
Count
Source
2234
242k
    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }
Unexecuted instantiation: _ZN8ImVectorI12ImTextureRefE8pop_backEv
Unexecuted instantiation: _ZN8ImVectorI12ImFontConfigE8pop_backEv
Unexecuted instantiation: _ZN8ImVectorIP12ImFontConfigE8pop_backEv
Unexecuted instantiation: _ZN8ImVectorIP6ImFontE8pop_backEv
Unexecuted instantiation: _ZN8ImVectorI22ImGuiTreeNodeStackDataE8pop_backEv
Unexecuted instantiation: _ZN8ImVectorI15ImGuiPtrOrIndexE8pop_backEv
2235
0
    inline void         push_front(const T& v)              { if (Size == 0) push_back(v); else insert(Data, v); }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE10push_frontERKS0_
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdE10push_frontERKS0_
Unexecuted instantiation: _ZN8ImVectorIP11ImGuiWindowE10push_frontERKS1_
2236
2
    inline T*           erase(const T* it)                  { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE5eraseEPKS0_
Unexecuted instantiation: _ZN8ImVectorIP11ImGuiWindowE5eraseEPKS1_
Unexecuted instantiation: _ZN8ImVectorI16ImGuiContextHookE5eraseEPKS0_
Unexecuted instantiation: _ZN8ImVectorIP13ImTextureDataE5eraseEPKS1_
_ZN8ImVectorIP11ImFontAtlasE5eraseEPKS1_
Line
Count
Source
2236
1
    inline T*           erase(const T* it)                  { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }
Unexecuted instantiation: _ZN8ImVectorI20ImGuiSettingsHandlerE5eraseEPKS0_
Unexecuted instantiation: _ZN8ImVectorIjE5eraseEPKj
Unexecuted instantiation: _ZN8ImVectorIiE5eraseEPKi
Unexecuted instantiation: _ZN8ImVectorIPcE5eraseEPKS0_
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdE5eraseEPKS0_
Unexecuted instantiation: _ZN8ImVectorI12ImFontConfigE5eraseEPKS0_
Unexecuted instantiation: _ZN8ImVectorIP6ImFontE5eraseEPKS1_
_ZN8ImVectorIP20ImDrawListSharedDataE5eraseEPKS1_
Line
Count
Source
2236
1
    inline T*           erase(const T* it)                  { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }
Unexecuted instantiation: _ZN8ImVectorI12ImGuiTabItemE5eraseEPKS0_
2237
1
    inline T*           erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last >= it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; }
2238
3
    inline T*           erase_unsorted(const T* it)         { IM_ASSERT(it >= Data && it < Data + Size);  const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }
Unexecuted instantiation: _ZN8ImVectorIiE14erase_unsortedEPKi
_ZN8ImVectorIP10ImDrawListE14erase_unsortedEPKS1_
Line
Count
Source
2238
3
    inline T*           erase_unsorted(const T* it)         { IM_ASSERT(it >= Data && it < Data + Size);  const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }
2239
4
    inline T*           insert(const T* it, const T& v)     { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }
Unexecuted instantiation: _ZN8ImVectorI21ImGuiListClipperRangeE6insertEPKS0_RS2_
Unexecuted instantiation: _ZN8ImVectorI9ImDrawCmdE6insertEPKS0_RS2_
Unexecuted instantiation: _ZN8ImVectorIP11ImGuiWindowE6insertEPKS1_RS3_
_ZN8ImVectorI16ImGuiStoragePairE6insertEPKS0_RS2_
Line
Count
Source
2239
4
    inline T*           insert(const T* it, const T& v)     { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }
2240
483k
    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
_ZNK8ImVectorIP11ImGuiWindowE8containsERKS1_
Line
Count
Source
2240
6
    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
Unexecuted instantiation: _ZNK8ImVectorIiE8containsERKi
_ZNK8ImVectorIP12ImFontConfigE8containsERKS1_
Line
Count
Source
2240
1
    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
_ZNK8ImVectorIP20ImDrawListSharedDataE8containsERKS1_
Line
Count
Source
2240
2
    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
_ZNK8ImVectorIjE8containsERKj
Line
Count
Source
2240
483k
    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }
2241
5
    inline T*           find(const T& v)                    { T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
Unexecuted instantiation: _ZN8ImVectorIP13ImTextureDataE4findERKS1_
_ZN8ImVectorIP11ImFontAtlasE4findERKS1_
Line
Count
Source
2241
1
    inline T*           find(const T& v)                    { T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
Unexecuted instantiation: _ZN8ImVectorIP11ImGuiWindowE4findERKS1_
Unexecuted instantiation: _ZN8ImVectorIiE4findERKi
_ZN8ImVectorIP10ImDrawListE4findERKS1_
Line
Count
Source
2241
3
    inline T*           find(const T& v)                    { T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
Unexecuted instantiation: _ZN8ImVectorIP6ImFontE4findERKS1_
_ZN8ImVectorIP20ImDrawListSharedDataE4findERKS1_
Line
Count
Source
2241
1
    inline T*           find(const T& v)                    { T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
2242
    inline const T*     find(const T& v) const              { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }
2243
    inline int          find_index(const T& v) const        { const T* data_end = Data + Size; const T* it = find(v); if (it == data_end) return -1; const ptrdiff_t off = it - Data; return (int)off; }
2244
2
    inline bool         find_erase(const T& v)              { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; }
Unexecuted instantiation: _ZN8ImVectorIP13ImTextureDataE10find_eraseERKS1_
_ZN8ImVectorIP11ImFontAtlasE10find_eraseERKS1_
Line
Count
Source
2244
1
    inline bool         find_erase(const T& v)              { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; }
Unexecuted instantiation: _ZN8ImVectorIP6ImFontE10find_eraseERKS1_
_ZN8ImVectorIP20ImDrawListSharedDataE10find_eraseERKS1_
Line
Count
Source
2244
1
    inline bool         find_erase(const T& v)              { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; }
2245
3
    inline bool         find_erase_unsorted(const T& v)     { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; }
Unexecuted instantiation: _ZN8ImVectorIiE19find_erase_unsortedERKi
_ZN8ImVectorIP10ImDrawListE19find_erase_unsortedERKS1_
Line
Count
Source
2245
3
    inline bool         find_erase_unsorted(const T& v)     { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; }
2246
0
    inline int          index_from_ptr(const T* it) const   { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; }
Unexecuted instantiation: _ZNK8ImVectorIP11ImGuiWindowE14index_from_ptrEPKS1_
Unexecuted instantiation: _ZNK8ImVectorI20ImFontAtlasRectEntryE14index_from_ptrEPKS0_
Unexecuted instantiation: _ZNK8ImVectorI18ImGuiOldColumnDataE14index_from_ptrEPKS0_
Unexecuted instantiation: _ZNK8ImVectorI12ImGuiTabItemE14index_from_ptrEPKS0_
Unexecuted instantiation: _ZNK8ImVectorI11ImFontGlyphE14index_from_ptrEPKS0_
Unexecuted instantiation: _ZNK8ImVectorIiE14index_from_ptrEPKi
2247
};
2248
IM_MSVC_RUNTIME_CHECKS_RESTORE
2249
2250
//-----------------------------------------------------------------------------
2251
// [SECTION] ImGuiStyle
2252
//-----------------------------------------------------------------------------
2253
// You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame().
2254
// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values,
2255
// and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors.
2256
//-----------------------------------------------------------------------------
2257
2258
struct ImGuiStyle
2259
{
2260
    // Font scaling
2261
    // - recap: ImGui::GetFontSize() == FontSizeBase * (FontScaleMain * FontScaleDpi * other_scaling_factors)
2262
    float       FontSizeBase;               // Current base font size before external global factors are applied. Use PushFont(NULL, size) to modify. Use ImGui::GetFontSize() to obtain scaled value.
2263
    float       FontScaleMain;              // Main global scale factor. May be set by application once, or exposed to end-user.
2264
    float       FontScaleDpi;               // Additional global scale factor from viewport/monitor contents scale. When io.ConfigDpiScaleFonts is enabled, this is automatically overwritten when changing monitor DPI.
2265
2266
    float       Alpha;                      // Global alpha applies to everything in Dear ImGui.
2267
    float       DisabledAlpha;              // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.
2268
    ImVec2      WindowPadding;              // Padding within a window.
2269
    float       WindowRounding;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.
2270
    float       WindowBorderSize;           // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
2271
    float       WindowBorderHoverPadding;   // Hit-testing extent outside/inside resizing border. Also extend determination of hovered window. Generally meaningfully larger than WindowBorderSize to make it easy to reach borders.
2272
    ImVec2      WindowMinSize;              // Minimum window size. This is a global setting. If you want to constrain individual windows, use SetNextWindowSizeConstraints().
2273
    ImVec2      WindowTitleAlign;           // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered.
2274
    ImGuiDir    WindowMenuButtonPosition;   // Side of the collapsing/docking button in the title bar (None/Left/Right). Defaults to ImGuiDir_Left.
2275
    float       ChildRounding;              // Radius of child window corners rounding. Set to 0.0f to have rectangular windows.
2276
    float       ChildBorderSize;            // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
2277
    float       PopupRounding;              // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding)
2278
    float       PopupBorderSize;            // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
2279
    ImVec2      FramePadding;               // Padding within a framed rectangle (used by most widgets).
2280
    float       FrameRounding;              // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets).
2281
    float       FrameBorderSize;            // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).
2282
    ImVec2      ItemSpacing;                // Horizontal and vertical spacing between widgets/lines.
2283
    ImVec2      ItemInnerSpacing;           // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label).
2284
    ImVec2      CellPadding;                // Padding within a table cell. Cellpadding.x is locked for entire table. CellPadding.y may be altered between different rows.
2285
    ImVec2      TouchExtraPadding;          // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!
2286
    float       IndentSpacing;              // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).
2287
    float       ColumnsMinSpacing;          // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).
2288
    float       ScrollbarSize;              // Width of the vertical scrollbar, Height of the horizontal scrollbar.
2289
    float       ScrollbarRounding;          // Radius of grab corners for scrollbar.
2290
    float       ScrollbarPadding;           // Padding of scrollbar grab within its frame (same for both axises).
2291
    float       GrabMinSize;                // Minimum width/height of a grab box for slider/scrollbar.
2292
    float       GrabRounding;               // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.
2293
    float       LogSliderDeadzone;          // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.
2294
    float       ImageBorderSize;            // Thickness of border around Image() calls.
2295
    float       TabRounding;                // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.
2296
    float       TabBorderSize;              // Thickness of border around tabs.
2297
    float       TabMinWidthBase;            // Minimum tab width, to make tabs larger than their contents. TabBar buttons are not affected.
2298
    float       TabMinWidthShrink;          // Minimum tab width after shrinking, when using ImGuiTabBarFlags_FittingPolicyMixed policy.
2299
    float       TabCloseButtonMinWidthSelected;     // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width.
2300
    float       TabCloseButtonMinWidthUnselected;   // -1: always visible. 0.0f: visible when hovered. >0.0f: visible when hovered if minimum width. FLT_MAX: never show close button when unselected.
2301
    float       TabBarBorderSize;           // Thickness of tab-bar separator, which takes on the tab active color to denote focus.
2302
    float       TabBarOverlineSize;         // Thickness of tab-bar overline, which highlights the selected tab-bar.
2303
    float       TableAngledHeadersAngle;    // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees).
2304
    ImVec2      TableAngledHeadersTextAlign;// Alignment of angled headers within the cell
2305
    ImGuiTreeNodeFlags TreeLinesFlags;      // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes.
2306
    float       TreeLinesSize;              // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines.
2307
    float       TreeLinesRounding;          // Radius of lines connecting child nodes to the vertical line.
2308
    ImGuiDir    ColorButtonPosition;        // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.
2309
    ImVec2      ButtonTextAlign;            // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).
2310
    ImVec2      SelectableTextAlign;        // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.
2311
    float       SeparatorTextBorderSize;    // Thickness of border in SeparatorText()
2312
    ImVec2      SeparatorTextAlign;         // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).
2313
    ImVec2      SeparatorTextPadding;       // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.
2314
    ImVec2      DisplayWindowPadding;       // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.
2315
    ImVec2      DisplaySafeAreaPadding;     // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).
2316
    float       MouseCursorScale;           // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later.
2317
    bool        AntiAliasedLines;           // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
2318
    bool        AntiAliasedLinesUseTex;     // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList).
2319
    bool        AntiAliasedFill;            // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).
2320
    float       CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
2321
    float       CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
2322
2323
    // Colors
2324
    ImVec4      Colors[ImGuiCol_COUNT];
2325
2326
    // Behaviors
2327
    // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO)
2328
    float             HoverStationaryDelay;     // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
2329
    float             HoverDelayShort;          // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
2330
    float             HoverDelayNormal;         // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
2331
    ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
2332
    ImGuiHoveredFlags HoverFlagsForTooltipNav;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
2333
2334
    // [Internal]
2335
    float       _MainScale;                 // FIXME-WIP: Reference scale, as applied by ScaleAllSizes().
2336
    float       _NextFrameFontSizeBase;     // FIXME: Temporary hack until we finish remaining work.
2337
2338
    // Functions
2339
    IMGUI_API   ImGuiStyle();
2340
    IMGUI_API   void ScaleAllSizes(float scale_factor); // Scale all spacing/padding/thickness values. Do not scale fonts.
2341
2342
    // Obsolete names
2343
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2344
    // TabMinWidthForCloseButton = TabCloseButtonMinWidthUnselected // Renamed in 1.91.9.
2345
#endif
2346
};
2347
2348
//-----------------------------------------------------------------------------
2349
// [SECTION] ImGuiIO
2350
//-----------------------------------------------------------------------------
2351
// Communicate most settings and inputs/outputs to Dear ImGui using this structure.
2352
// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage.
2353
// It is generally expected that:
2354
// - initialization: backends and user code writes to ImGuiIO.
2355
// - main loop: backends writes to ImGuiIO, user code and imgui code reads from ImGuiIO.
2356
//-----------------------------------------------------------------------------
2357
// Also see ImGui::GetPlatformIO() and ImGuiPlatformIO struct for OS/platform related functions: clipboard, IME etc.
2358
//-----------------------------------------------------------------------------
2359
2360
// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.
2361
// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration.
2362
struct ImGuiKeyData
2363
{
2364
    bool        Down;               // True for if key is down
2365
    float       DownDuration;       // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held)
2366
    float       DownDurationPrev;   // Last frame duration the key has been down
2367
    float       AnalogValue;        // 0.0f..1.0f for gamepad values
2368
};
2369
2370
struct ImGuiIO
2371
{
2372
    //------------------------------------------------------------------
2373
    // Configuration                            // Default value
2374
    //------------------------------------------------------------------
2375
2376
    ImGuiConfigFlags   ConfigFlags;             // = 0              // See ImGuiConfigFlags_ enum. Set by user/application. Keyboard/Gamepad navigation options, etc.
2377
    ImGuiBackendFlags  BackendFlags;            // = 0              // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend.
2378
    ImVec2      DisplaySize;                    // <unset>          // Main display size, in pixels (== GetMainViewport()->Size). May change every frame.
2379
    ImVec2      DisplayFramebufferScale;        // = (1, 1)         // Main display density. For retina display where window coordinates are different from framebuffer coordinates. This will affect font density + will end up in ImDrawData::FramebufferScale.
2380
    float       DeltaTime;                      // = 1.0f/60.0f     // Time elapsed since last frame, in seconds. May change every frame.
2381
    float       IniSavingRate;                  // = 5.0f           // Minimum time between saving positions/sizes to .ini file, in seconds.
2382
    const char* IniFilename;                    // = "imgui.ini"    // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions.
2383
    const char* LogFilename;                    // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).
2384
    void*       UserData;                       // = NULL           // Store your own data.
2385
2386
    // Font system
2387
    ImFontAtlas*Fonts;                          // <auto>           // Font atlas: load, rasterize and pack one or more fonts into a single texture.
2388
    ImFont*     FontDefault;                    // = NULL           // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].
2389
    bool        FontAllowUserScaling;           // = false          // [OBSOLETE] Allow user scaling text of individual window with CTRL+Wheel.
2390
2391
    // Keyboard/Gamepad Navigation options
2392
    bool        ConfigNavSwapGamepadButtons;    // = false          // Swap Activate<>Cancel (A<>B) buttons, matching typical "Nintendo/Japanese style" gamepad layout.
2393
    bool        ConfigNavMoveSetMousePos;       // = false          // Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult. Will update io.MousePos and set io.WantSetMousePos=true.
2394
    bool        ConfigNavCaptureKeyboard;       // = true           // Sets io.WantCaptureKeyboard when io.NavActive is set.
2395
    bool        ConfigNavEscapeClearFocusItem;  // = true           // Pressing Escape can clear focused item + navigation id/highlight. Set to false if you want to always keep highlight on.
2396
    bool        ConfigNavEscapeClearFocusWindow;// = false          // Pressing Escape can clear focused window as well (super set of io.ConfigNavEscapeClearFocusItem).
2397
    bool        ConfigNavCursorVisibleAuto;     // = true           // Using directional navigation key makes the cursor visible. Mouse click hides the cursor.
2398
    bool        ConfigNavCursorVisibleAlways;   // = false          // Navigation cursor is always visible.
2399
2400
    // Miscellaneous options
2401
    // (you can visualize and interact with all options in 'Demo->Configuration')
2402
    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.
2403
    bool        ConfigMacOSXBehaviors;          // = defined(__APPLE__) // Swap Cmd<>Ctrl keys + OS X style text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.
2404
    bool        ConfigInputTrickleEventQueue;   // = true           // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.
2405
    bool        ConfigInputTextCursorBlink;     // = true           // Enable blinking cursor (optional as some users consider it to be distracting).
2406
    bool        ConfigInputTextEnterKeepActive; // = false          // [BETA] Pressing Enter will keep item active and select contents (single-line only).
2407
    bool        ConfigDragClickToInputText;     // = false          // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.
2408
    bool        ConfigWindowsResizeFromEdges;   // = true           // Enable resizing of windows from their edges and from the lower-left corner. This requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)
2409
    bool        ConfigWindowsMoveFromTitleBarOnly;  // = false      // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.
2410
    bool        ConfigWindowsCopyContentsWithCtrlC; // = false      // [EXPERIMENTAL] CTRL+C copy the contents of focused window into the clipboard. Experimental because: (1) has known issues with nested Begin/End pairs (2) text output quality varies (3) text output is in submission order rather than spatial order.
2411
    bool        ConfigScrollbarScrollByPage;    // = true           // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location.
2412
    float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.
2413
2414
    // Inputs Behaviors
2415
    // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)
2416
    float       MouseDoubleClickTime;           // = 0.30f          // Time for a double-click, in seconds.
2417
    float       MouseDoubleClickMaxDist;        // = 6.0f           // Distance threshold to stay in to validate a double-click, in pixels.
2418
    float       MouseDragThreshold;             // = 6.0f           // Distance threshold before considering we are dragging.
2419
    float       KeyRepeatDelay;                 // = 0.275f         // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).
2420
    float       KeyRepeatRate;                  // = 0.050f         // When holding a key/button, rate at which it repeats, in seconds.
2421
2422
    //------------------------------------------------------------------
2423
    // Debug options
2424
    //------------------------------------------------------------------
2425
2426
    // Options to configure Error Handling and how we handle recoverable errors [EXPERIMENTAL]
2427
    // - Error recovery is provided as a way to facilitate:
2428
    //    - Recovery after a programming error (native code or scripting language - the later tends to facilitate iterating on code while running).
2429
    //    - Recovery after running an exception handler or any error processing which may skip code after an error has been detected.
2430
    // - Error recovery is not perfect nor guaranteed! It is a feature to ease development.
2431
    //   You not are not supposed to rely on it in the course of a normal application run.
2432
    // - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT().
2433
    // - By design, we do NOT allow error recovery to be 100% silent. One of the three options needs to be checked!
2434
    // - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled when making direct imgui API calls!
2435
    //   Otherwise it would severely hinder your ability to catch and correct mistakes!
2436
    // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details.
2437
    // - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!)
2438
    // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (tooltips, visible log entries, use callback etc.)
2439
    // - Recovery after error/exception: record stack sizes with ErrorRecoveryStoreState(), disable assert, set log callback (to e.g. trigger high-level breakpoint), recover with ErrorRecoveryTryToRecoverState(), restore settings.
2440
    bool        ConfigErrorRecovery;                // = true       // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled.
2441
    bool        ConfigErrorRecoveryEnableAssert;    // = true       // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR()
2442
    bool        ConfigErrorRecoveryEnableDebugLog;  // = true       // Enable debug log output on recoverable errors.
2443
    bool        ConfigErrorRecoveryEnableTooltip;   // = true       // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled.
2444
2445
    // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
2446
    // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
2447
    // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
2448
    //   e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version.
2449
    bool        ConfigDebugIsDebuggerPresent;   // = false          // Enable various tools calling IM_DEBUG_BREAK().
2450
2451
    // Tools to detect code submitting items with conflicting/duplicate IDs
2452
    // - Code should use PushID()/PopID() in loops, or append "##xx" to same-label identifiers.
2453
    // - Empty label e.g. Button("") == same ID as parent widget/node. Use Button("##xx") instead!
2454
    // - See FAQ https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-about-the-id-stack-system
2455
    bool        ConfigDebugHighlightIdConflicts;// = true           // Highlight and show an error message popup when multiple items have conflicting identifiers.
2456
    bool        ConfigDebugHighlightIdConflictsShowItemPicker;//=true // Show "Item Picker" button in aforementioned popup.
2457
2458
    // Tools to test correct Begin/End and BeginChild/EndChild behaviors.
2459
    // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
2460
    // - This is inconsistent with other BeginXXX functions and create confusion for many users.
2461
    // - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
2462
    bool        ConfigDebugBeginReturnValueOnce;// = false          // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.
2463
    bool        ConfigDebugBeginReturnValueLoop;// = false          // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running.
2464
2465
    // Option to deactivate io.AddFocusEvent(false) handling.
2466
    // - May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
2467
    // - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
2468
    bool        ConfigDebugIgnoreFocusLoss;     // = false          // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys()/io.ClearInputMouse() in input processing.
2469
2470
    // Option to audit .ini data
2471
    bool        ConfigDebugIniSettings;         // = false          // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)
2472
2473
    //------------------------------------------------------------------
2474
    // Platform Identifiers
2475
    // (the imgui_impl_xxxx backend files are setting those up for you)
2476
    //------------------------------------------------------------------
2477
2478
    // Nowadays those would be stored in ImGuiPlatformIO but we are leaving them here for legacy reasons.
2479
    // Optional: Platform/Renderer backend name (informational only! will be displayed in About Window) + User data for backend/wrappers to store their own stuff.
2480
    const char* BackendPlatformName;            // = NULL
2481
    const char* BackendRendererName;            // = NULL
2482
    void*       BackendPlatformUserData;        // = NULL           // User data for platform backend
2483
    void*       BackendRendererUserData;        // = NULL           // User data for renderer backend
2484
    void*       BackendLanguageUserData;        // = NULL           // User data for non C++ programming language backend
2485
2486
    //------------------------------------------------------------------
2487
    // Input - Call before calling NewFrame()
2488
    //------------------------------------------------------------------
2489
2490
    // Input Functions
2491
    IMGUI_API void  AddKeyEvent(ImGuiKey key, bool down);                   // Queue a new key down/up event. Key should be "translated" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)
2492
    IMGUI_API void  AddKeyAnalogEvent(ImGuiKey key, bool down, float v);    // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend.
2493
    IMGUI_API void  AddMousePosEvent(float x, float y);                     // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered)
2494
    IMGUI_API void  AddMouseButtonEvent(int button, bool down);             // Queue a mouse button change
2495
    IMGUI_API void  AddMouseWheelEvent(float wheel_x, float wheel_y);       // Queue a mouse wheel update. wheel_y<0: scroll down, wheel_y>0: scroll up, wheel_x<0: scroll right, wheel_x>0: scroll left.
2496
    IMGUI_API void  AddMouseSourceEvent(ImGuiMouseSource source);           // Queue a mouse source change (Mouse/TouchScreen/Pen)
2497
    IMGUI_API void  AddFocusEvent(bool focused);                            // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window)
2498
    IMGUI_API void  AddInputCharacter(unsigned int c);                      // Queue a new character input
2499
    IMGUI_API void  AddInputCharacterUTF16(ImWchar16 c);                    // Queue a new character input from a UTF-16 character, it can be a surrogate
2500
    IMGUI_API void  AddInputCharactersUTF8(const char* str);                // Queue a new characters input from a UTF-8 string
2501
2502
    IMGUI_API void  SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode.
2503
    IMGUI_API void  SetAppAcceptingEvents(bool accepting_events);           // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.
2504
    IMGUI_API void  ClearEventsQueue();                                     // Clear all incoming events.
2505
    IMGUI_API void  ClearInputKeys();                                       // Clear current keyboard/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.
2506
    IMGUI_API void  ClearInputMouse();                                      // Clear current mouse state.
2507
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2508
    IMGUI_API void  ClearInputCharacters();                                 // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys().
2509
#endif
2510
2511
    //------------------------------------------------------------------
2512
    // Output - Updated by NewFrame() or EndFrame()/Render()
2513
    // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is
2514
    //  generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!)
2515
    //------------------------------------------------------------------
2516
2517
    bool        WantCaptureMouse;                   // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
2518
    bool        WantCaptureKeyboard;                // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
2519
    bool        WantTextInput;                      // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
2520
    bool        WantSetMousePos;                    // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when io.ConfigNavMoveSetMousePos is enabled.
2521
    bool        WantSaveIniSettings;                // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
2522
    bool        NavActive;                          // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
2523
    bool        NavVisible;                         // Keyboard/Gamepad navigation highlight is visible and allowed (will handle ImGuiKey_NavXXX events).
2524
    float       Framerate;                          // Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally.
2525
    int         MetricsRenderVertices;              // Vertices output during last call to Render()
2526
    int         MetricsRenderIndices;               // Indices output during last call to Render() = number of triangles * 3
2527
    int         MetricsRenderWindows;               // Number of visible windows
2528
    int         MetricsActiveWindows;               // Number of active windows
2529
    ImVec2      MouseDelta;                         // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.
2530
2531
    //------------------------------------------------------------------
2532
    // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed!
2533
    //------------------------------------------------------------------
2534
2535
    ImGuiContext* Ctx;                              // Parent UI context (needs to be set explicitly by parent).
2536
2537
    // Main Input State
2538
    // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead)
2539
    // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere)
2540
    ImVec2      MousePos;                           // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)
2541
    bool        MouseDown[5];                       // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
2542
    float       MouseWheel;                         // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold SHIFT to turn vertical scroll into horizontal scroll.
2543
    float       MouseWheelH;                        // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends.
2544
    ImGuiMouseSource MouseSource;                   // Mouse actual input peripheral (Mouse/TouchScreen/Pen).
2545
    bool        KeyCtrl;                            // Keyboard modifier down: Ctrl (non-macOS), Cmd (macOS)
2546
    bool        KeyShift;                           // Keyboard modifier down: Shift
2547
    bool        KeyAlt;                             // Keyboard modifier down: Alt
2548
    bool        KeySuper;                           // Keyboard modifier down: Windows/Super (non-macOS), Ctrl (macOS)
2549
2550
    // Other state maintained from data above + IO function calls
2551
    ImGuiKeyChord KeyMods;                          // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags). Read-only, updated by NewFrame()
2552
    ImGuiKeyData  KeysData[ImGuiKey_NamedKey_COUNT];// Key state for all known keys. MUST use 'key - ImGuiKey_NamedKey_BEGIN' as index. Use IsKeyXXX() functions to access this.
2553
    bool        WantCaptureMouseUnlessPopupClose;   // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.
2554
    ImVec2      MousePosPrev;                       // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)
2555
    ImVec2      MouseClickedPos[5];                 // Position at time of clicking
2556
    double      MouseClickedTime[5];                // Time of last click (used to figure out double-click)
2557
    bool        MouseClicked[5];                    // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0)
2558
    bool        MouseDoubleClicked[5];              // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2)
2559
    ImU16       MouseClickedCount[5];               // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down
2560
    ImU16       MouseClickedLastCount[5];           // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.
2561
    bool        MouseReleased[5];                   // Mouse button went from Down to !Down
2562
    double      MouseReleasedTime[5];               // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click).
2563
    bool        MouseDownOwned[5];                  // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.
2564
    bool        MouseDownOwnedUnlessPopupClose[5];  // Track if button was clicked inside a dear imgui window.
2565
    bool        MouseWheelRequestAxisSwap;          // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.
2566
    bool        MouseCtrlLeftAsRightClick;          // (OSX) Set to true when the current click was a Ctrl+click that spawned a simulated right click
2567
    float       MouseDownDuration[5];               // Duration the mouse button has been down (0.0f == just clicked)
2568
    float       MouseDownDurationPrev[5];           // Previous time the mouse button has been down
2569
    float       MouseDragMaxDistanceSqr[5];         // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)
2570
    float       PenPressure;                        // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.
2571
    bool        AppFocusLost;                       // Only modify via AddFocusEvent()
2572
    bool        AppAcceptingEvents;                 // Only modify via SetAppAcceptingEvents()
2573
    ImWchar16   InputQueueSurrogate;                // For AddInputCharacterUTF16()
2574
    ImVector<ImWchar> InputQueueCharacters;         // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.
2575
2576
    // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.
2577
    // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().
2578
    //   Old (<1.87):  ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
2579
    //   Old (<1.87):  ImGui::IsKeyPressed(MYPLATFORM_KEY_SPACE)                  --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)
2580
    // Read https://github.com/ocornut/imgui/issues/4921 for details.
2581
    //int       KeyMap[ImGuiKey_COUNT];             // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.
2582
    //bool      KeysDown[ImGuiKey_COUNT];           // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.
2583
    //float     NavInputs[ImGuiNavInput_COUNT];     // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.
2584
    //void*     ImeWindowHandle;                    // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.
2585
2586
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2587
    float       FontGlobalScale;                    // Moved io.FontGlobalScale to style.FontScaleMain in 1.92 (June 2025)
2588
2589
    // Legacy: before 1.91.1, clipboard functions were stored in ImGuiIO instead of ImGuiPlatformIO.
2590
    // As this is will affect all users of custom engines/backends, we are providing proper legacy redirection (will obsolete).
2591
    const char* (*GetClipboardTextFn)(void* user_data);
2592
    void        (*SetClipboardTextFn)(void* user_data, const char* text);
2593
    void*       ClipboardUserData;
2594
#endif
2595
2596
    IMGUI_API   ImGuiIO();
2597
};
2598
2599
//-----------------------------------------------------------------------------
2600
// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload)
2601
//-----------------------------------------------------------------------------
2602
2603
// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used.
2604
// The callback function should return 0 by default.
2605
// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details)
2606
// - ImGuiInputTextFlags_CallbackEdit:        Callback on buffer edit. Note that InputText() already returns true on edit + you can always use IsItemEdited(). The callback is useful to manipulate the underlying buffer while focus is active.
2607
// - ImGuiInputTextFlags_CallbackAlways:      Callback on each iteration
2608
// - ImGuiInputTextFlags_CallbackCompletion:  Callback on pressing TAB
2609
// - ImGuiInputTextFlags_CallbackHistory:     Callback on pressing Up/Down arrows
2610
// - ImGuiInputTextFlags_CallbackCharFilter:  Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.
2611
// - ImGuiInputTextFlags_CallbackResize:      Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow.
2612
struct ImGuiInputTextCallbackData
2613
{
2614
    ImGuiContext*       Ctx;            // Parent UI context
2615
    ImGuiInputTextFlags EventFlag;      // One ImGuiInputTextFlags_Callback*    // Read-only
2616
    ImGuiInputTextFlags Flags;          // What user passed to InputText()      // Read-only
2617
    void*               UserData;       // What user passed to InputText()      // Read-only
2618
2619
    // Arguments for the different callback events
2620
    // - During Resize callback, Buf will be same as your input buffer.
2621
    // - However, during Completion/History/Always callback, Buf always points to our own internal data (it is not the same as your buffer)! Changes to it will be reflected into your own buffer shortly after the callback.
2622
    // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary.
2623
    // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state.
2624
    ImWchar             EventChar;      // Character input                      // Read-write   // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0;
2625
    ImGuiKey            EventKey;       // Key pressed (Up/Down/TAB)            // Read-only    // [Completion,History]
2626
    char*               Buf;            // Text buffer                          // Read-write   // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!
2627
    int                 BufTextLen;     // Text length (in bytes)               // Read-write   // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()
2628
    int                 BufSize;        // Buffer size (in bytes) = capacity+1  // Read-only    // [Resize,Completion,History,Always] Include zero-terminator storage. In C land: == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1
2629
    bool                BufDirty;       // Set if you modify Buf/BufTextLen!    // Write        // [Completion,History,Always]
2630
    int                 CursorPos;      //                                      // Read-write   // [Completion,History,Always]
2631
    int                 SelectionStart; //                                      // Read-write   // [Completion,History,Always] == to SelectionEnd when no selection
2632
    int                 SelectionEnd;   //                                      // Read-write   // [Completion,History,Always]
2633
2634
    // Helper functions for text manipulation.
2635
    // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.
2636
    IMGUI_API ImGuiInputTextCallbackData();
2637
    IMGUI_API void      DeleteChars(int pos, int bytes_count);
2638
    IMGUI_API void      InsertChars(int pos, const char* text, const char* text_end = NULL);
2639
0
    void                SelectAll()             { SelectionStart = 0; SelectionEnd = BufTextLen; }
2640
0
    void                ClearSelection()        { SelectionStart = SelectionEnd = BufTextLen; }
2641
0
    bool                HasSelection() const    { return SelectionStart != SelectionEnd; }
2642
};
2643
2644
// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin().
2645
// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough.
2646
struct ImGuiSizeCallbackData
2647
{
2648
    void*   UserData;       // Read-only.   What user passed to SetNextWindowSizeConstraints(). Generally store an integer or float in here (need reinterpret_cast<>).
2649
    ImVec2  Pos;            // Read-only.   Window position, for reference.
2650
    ImVec2  CurrentSize;    // Read-only.   Current window size.
2651
    ImVec2  DesiredSize;    // Read-write.  Desired size, based on user's mouse position. Write to this field to restrain resizing.
2652
};
2653
2654
// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload()
2655
struct ImGuiPayload
2656
{
2657
    // Members
2658
    void*           Data;               // Data (copied and owned by dear imgui)
2659
    int             DataSize;           // Data size
2660
2661
    // [Internal]
2662
    ImGuiID         SourceId;           // Source item id
2663
    ImGuiID         SourceParentId;     // Source parent id (if available)
2664
    int             DataFrameCount;     // Data timestamp
2665
    char            DataType[32 + 1];   // Data type tag (short user-supplied string, 32 characters max)
2666
    bool            Preview;            // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets)
2667
    bool            Delivery;           // Set when AcceptDragDropPayload() was called and mouse button is released over the target item.
2668
2669
1
    ImGuiPayload()  { Clear(); }
2670
1
    void Clear()    { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; }
2671
0
    bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; }
2672
0
    bool IsPreview() const                  { return Preview; }
2673
0
    bool IsDelivery() const                 { return Delivery; }
2674
};
2675
2676
//-----------------------------------------------------------------------------
2677
// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)
2678
//-----------------------------------------------------------------------------
2679
2680
// Helper: Unicode defines
2681
1
#define IM_UNICODE_CODEPOINT_INVALID 0xFFFD     // Invalid Unicode code point (standard value).
2682
#ifdef IMGUI_USE_WCHAR32
2683
#define IM_UNICODE_CODEPOINT_MAX     0x10FFFF   // Maximum Unicode code point supported by this build.
2684
#else
2685
0
#define IM_UNICODE_CODEPOINT_MAX     0xFFFF     // Maximum Unicode code point supported by this build.
2686
#endif
2687
2688
// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create a UI within deep-nested code that runs multiple times every frame.
2689
// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame");
2690
struct ImGuiOnceUponAFrame
2691
{
2692
0
    ImGuiOnceUponAFrame() { RefFrame = -1; }
2693
    mutable int RefFrame;
2694
0
    operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; }
2695
};
2696
2697
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
2698
struct ImGuiTextFilter
2699
{
2700
    IMGUI_API           ImGuiTextFilter(const char* default_filter = "");
2701
    IMGUI_API bool      Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f);  // Helper calling InputText+Build
2702
    IMGUI_API bool      PassFilter(const char* text, const char* text_end = NULL) const;
2703
    IMGUI_API void      Build();
2704
0
    void                Clear()          { InputBuf[0] = 0; Build(); }
2705
0
    bool                IsActive() const { return !Filters.empty(); }
2706
2707
    // [Internal]
2708
    struct ImGuiTextRange
2709
    {
2710
        const char*     b;
2711
        const char*     e;
2712
2713
0
        ImGuiTextRange()                                { b = e = NULL; }
2714
0
        ImGuiTextRange(const char* _b, const char* _e)  { b = _b; e = _e; }
2715
0
        bool            empty() const                   { return b == e; }
2716
        IMGUI_API void  split(char separator, ImVector<ImGuiTextRange>* out) const;
2717
    };
2718
    char                    InputBuf[256];
2719
    ImVector<ImGuiTextRange>Filters;
2720
    int                     CountGrep;
2721
};
2722
2723
// Helper: Growable text buffer for logging/accumulating text
2724
// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder')
2725
struct ImGuiTextBuffer
2726
{
2727
    ImVector<char>      Buf;
2728
    IMGUI_API static char EmptyString[1];
2729
2730
5
    ImGuiTextBuffer()   { }
2731
0
    inline char         operator[](int i) const { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; }
2732
0
    const char*         begin() const           { return Buf.Data ? &Buf.front() : EmptyString; }
2733
0
    const char*         end() const             { return Buf.Data ? &Buf.back() : EmptyString; } // Buf is zero-terminated, so end() will point on the zero-terminator
2734
28
    int                 size() const            { return Buf.Size ? Buf.Size - 1 : 0; }
2735
0
    bool                empty() const           { return Buf.Size <= 1; }
2736
2
    void                clear()                 { Buf.clear(); }
2737
0
    void                resize(int size)        { if (Buf.Size > size) Buf.Data[size] = 0; Buf.resize(size ? size + 1 : 0, 0); } // Similar to resize(0) on ImVector: empty string but don't free buffer.
2738
27
    void                reserve(int capacity)   { Buf.reserve(capacity); }
2739
1
    const char*         c_str() const           { return Buf.Data ? Buf.Data : EmptyString; }
2740
    IMGUI_API void      append(const char* str, const char* str_end = NULL);
2741
    IMGUI_API void      appendf(const char* fmt, ...) IM_FMTARGS(2);
2742
    IMGUI_API void      appendfv(const char* fmt, va_list args) IM_FMTLIST(2);
2743
};
2744
2745
// [Internal] Key+Value for ImGuiStorage
2746
struct ImGuiStoragePair
2747
{
2748
    ImGuiID     key;
2749
    union       { int val_i; float val_f; void* val_p; };
2750
0
    ImGuiStoragePair(ImGuiID _key, int _val)    { key = _key; val_i = _val; }
2751
0
    ImGuiStoragePair(ImGuiID _key, float _val)  { key = _key; val_f = _val; }
2752
4
    ImGuiStoragePair(ImGuiID _key, void* _val)  { key = _key; val_p = _val; }
2753
};
2754
2755
// Helper: Key->Value storage
2756
// Typically you don't have to worry about this since a storage is held within each Window.
2757
// We use it to e.g. store collapse state for a tree (Int 0/1)
2758
// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame)
2759
// You can use it as custom user storage for temporary values. Declare your own storage if, for example:
2760
// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state).
2761
// - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient)
2762
// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types.
2763
struct ImGuiStorage
2764
{
2765
    // [Internal]
2766
    ImVector<ImGuiStoragePair>      Data;
2767
2768
    // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N)
2769
    // - Set***() functions find pair, insertion on demand if missing.
2770
    // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair.
2771
7
    void                Clear() { Data.clear(); }
2772
    IMGUI_API int       GetInt(ImGuiID key, int default_val = 0) const;
2773
    IMGUI_API void      SetInt(ImGuiID key, int val);
2774
    IMGUI_API bool      GetBool(ImGuiID key, bool default_val = false) const;
2775
    IMGUI_API void      SetBool(ImGuiID key, bool val);
2776
    IMGUI_API float     GetFloat(ImGuiID key, float default_val = 0.0f) const;
2777
    IMGUI_API void      SetFloat(ImGuiID key, float val);
2778
    IMGUI_API void*     GetVoidPtr(ImGuiID key) const; // default_val is NULL
2779
    IMGUI_API void      SetVoidPtr(ImGuiID key, void* val);
2780
2781
    // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set.
2782
    // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.
2783
    // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct)
2784
    //      float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar;
2785
    IMGUI_API int*      GetIntRef(ImGuiID key, int default_val = 0);
2786
    IMGUI_API bool*     GetBoolRef(ImGuiID key, bool default_val = false);
2787
    IMGUI_API float*    GetFloatRef(ImGuiID key, float default_val = 0.0f);
2788
    IMGUI_API void**    GetVoidPtrRef(ImGuiID key, void* default_val = NULL);
2789
2790
    // Advanced: for quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.
2791
    IMGUI_API void      BuildSortByKey();
2792
    // Obsolete: use on your own storage if you know only integer are being stored (open/close all tree nodes)
2793
    IMGUI_API void      SetAllInt(int val);
2794
2795
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2796
    //typedef ::ImGuiStoragePair ImGuiStoragePair;  // 1.90.8: moved type outside struct
2797
#endif
2798
};
2799
2800
// Flags for ImGuiListClipper (currently not fully exposed in function calls: a future refactor will likely add this to ImGuiListClipper::Begin function equivalent)
2801
enum ImGuiListClipperFlags_
2802
{
2803
    ImGuiListClipperFlags_None                  = 0,
2804
    ImGuiListClipperFlags_NoSetTableRowCounters = 1 << 0,   // [Internal] Disabled modifying table row counters. Avoid assumption that 1 clipper item == 1 table row.
2805
};
2806
2807
// Helper: Manually clip large list of items.
2808
// If you have lots evenly spaced items and you have random access to the list, you can perform coarse
2809
// clipping based on visibility to only submit items that are in view.
2810
// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped.
2811
// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally
2812
//  fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily
2813
//  scale using lists with tens of thousands of items without a problem)
2814
// Usage:
2815
//   ImGuiListClipper clipper;
2816
//   clipper.Begin(1000);         // We have 1000 elements, evenly spaced.
2817
//   while (clipper.Step())
2818
//       for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
2819
//           ImGui::Text("line number %d", i);
2820
// Generally what happens is:
2821
// - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not.
2822
// - User code submit that one element.
2823
// - Clipper can measure the height of the first element
2824
// - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element.
2825
// - User code submit visible elements.
2826
// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc.
2827
struct ImGuiListClipper
2828
{
2829
    ImGuiContext*   Ctx;                // Parent UI context
2830
    int             DisplayStart;       // First item to display, updated by each call to Step()
2831
    int             DisplayEnd;         // End of items to display (exclusive)
2832
    int             ItemsCount;         // [Internal] Number of items
2833
    float           ItemsHeight;        // [Internal] Height of item after a first step and item submission can calculate it
2834
    double          StartPosY;          // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed
2835
    double          StartSeekOffsetY;   // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows.
2836
    void*           TempData;           // [Internal] Internal data
2837
    ImGuiListClipperFlags Flags;        // [Internal] Flags, currently not yet well exposed.
2838
2839
    // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need)
2840
    // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().
2841
    IMGUI_API ImGuiListClipper();
2842
    IMGUI_API ~ImGuiListClipper();
2843
    IMGUI_API void  Begin(int items_count, float items_height = -1.0f);
2844
    IMGUI_API void  End();             // Automatically called on the last call of Step() that returns false.
2845
    IMGUI_API bool  Step();            // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items.
2846
2847
    // Call IncludeItemByIndex() or IncludeItemsByIndex() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility.
2848
    // (Due to alignment / padding of certain items it is possible that an extra item may be included on either end of the display range).
2849
0
    inline void     IncludeItemByIndex(int item_index)                  { IncludeItemsByIndex(item_index, item_index + 1); }
2850
    IMGUI_API void  IncludeItemsByIndex(int item_begin, int item_end);  // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped.
2851
2852
    // Seek cursor toward given item. This is automatically called while stepping.
2853
    // - The only reason to call this is: you can use ImGuiListClipper::Begin(INT_MAX) if you don't know item count ahead of time.
2854
    // - In this case, after all steps are done, you'll want to call SeekCursorForItem(item_count).
2855
    IMGUI_API void  SeekCursorForItem(int item_index);
2856
2857
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2858
    //inline void IncludeRangeByIndices(int item_begin, int item_end)      { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]
2859
    //inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6]
2860
    //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79]
2861
#endif
2862
};
2863
2864
// Helpers: ImVec2/ImVec4 operators
2865
// - It is important that we are keeping those disabled by default so they don't leak in user space.
2866
// - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h)
2867
// - Add '#define IMGUI_DEFINE_MATH_OPERATORS' before including this file (or in imconfig.h) to access courtesy maths operators for ImVec2 and ImVec4.
2868
// - We intentionally provide ImVec2*float but not float*ImVec2: this is rare enough and we want to reduce the surface for possible user mistake.
2869
#ifdef IMGUI_DEFINE_MATH_OPERATORS
2870
#define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED
2871
IM_MSVC_RUNTIME_CHECKS_OFF
2872
// ImVec2 operators
2873
732k
inline ImVec2  operator*(const ImVec2& lhs, const float rhs)    { return ImVec2(lhs.x * rhs, lhs.y * rhs); }
2874
0
inline ImVec2  operator/(const ImVec2& lhs, const float rhs)    { return ImVec2(lhs.x / rhs, lhs.y / rhs); }
2875
1.94M
inline ImVec2  operator+(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }
2876
733k
inline ImVec2  operator-(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); }
2877
70
inline ImVec2  operator*(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
2878
0
inline ImVec2  operator/(const ImVec2& lhs, const ImVec2& rhs)  { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); }
2879
0
inline ImVec2  operator-(const ImVec2& lhs)                     { return ImVec2(-lhs.x, -lhs.y); }
2880
0
inline ImVec2& operator*=(ImVec2& lhs, const float rhs)         { lhs.x *= rhs; lhs.y *= rhs; return lhs; }
2881
0
inline ImVec2& operator/=(ImVec2& lhs, const float rhs)         { lhs.x /= rhs; lhs.y /= rhs; return lhs; }
2882
4.49k
inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }
2883
0
inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }
2884
0
inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; }
2885
0
inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs)       { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }
2886
0
inline bool    operator==(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; }
2887
0
inline bool    operator!=(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; }
2888
// ImVec4 operators
2889
0
inline ImVec4  operator*(const ImVec4& lhs, const float rhs)    { return ImVec4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); }
2890
0
inline ImVec4  operator/(const ImVec4& lhs, const float rhs)    { return ImVec4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs); }
2891
0
inline ImVec4  operator+(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }
2892
0
inline ImVec4  operator-(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }
2893
0
inline ImVec4  operator*(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }
2894
0
inline ImVec4  operator/(const ImVec4& lhs, const ImVec4& rhs)  { return ImVec4(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w); }
2895
0
inline ImVec4  operator-(const ImVec4& lhs)                     { return ImVec4(-lhs.x, -lhs.y, -lhs.z, -lhs.w); }
2896
0
inline bool    operator==(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w; }
2897
0
inline bool    operator!=(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w; }
2898
IM_MSVC_RUNTIME_CHECKS_RESTORE
2899
#endif
2900
2901
// Helpers macros to generate 32-bit encoded colors
2902
// - User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file.
2903
// - Any setting other than the default will need custom backend support. The only standard backend that supports anything else than the default is DirectX9.
2904
#ifndef IM_COL32_R_SHIFT
2905
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
2906
#define IM_COL32_R_SHIFT    16
2907
#define IM_COL32_G_SHIFT    8
2908
#define IM_COL32_B_SHIFT    0
2909
#define IM_COL32_A_SHIFT    24
2910
#define IM_COL32_A_MASK     0xFF000000
2911
#else
2912
981k
#define IM_COL32_R_SHIFT    0
2913
981k
#define IM_COL32_G_SHIFT    8
2914
981k
#define IM_COL32_B_SHIFT    16
2915
981k
#define IM_COL32_A_SHIFT    24
2916
1.86M
#define IM_COL32_A_MASK     0xFF000000
2917
#endif
2918
#endif
2919
8.47k
#define IM_COL32(R,G,B,A)    (((ImU32)(A)<<IM_COL32_A_SHIFT) | ((ImU32)(B)<<IM_COL32_B_SHIFT) | ((ImU32)(G)<<IM_COL32_G_SHIFT) | ((ImU32)(R)<<IM_COL32_R_SHIFT))
2920
1.65k
#define IM_COL32_WHITE       IM_COL32(255,255,255,255)  // Opaque white = 0xFFFFFFFF
2921
0
#define IM_COL32_BLACK       IM_COL32(0,0,0,255)        // Opaque black
2922
5.46k
#define IM_COL32_BLACK_TRANS IM_COL32(0,0,0,0)          // Transparent black = 0x00000000
2923
2924
// Helper: ImColor() implicitly converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float)
2925
// Prefer using IM_COL32() macros if you want a guaranteed compile-time ImU32 for usage with ImDrawList API.
2926
// **Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. MAY OBSOLETE.
2927
// **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed.
2928
struct ImColor
2929
{
2930
    ImVec4          Value;
2931
2932
0
    constexpr ImColor()                                             { }
2933
0
    constexpr ImColor(float r, float g, float b, float a = 1.0f)    : Value(r, g, b, a) { }
2934
0
    constexpr ImColor(const ImVec4& col)                            : Value(col) {}
2935
0
    constexpr ImColor(int r, int g, int b, int a = 255)             : Value((float)r * (1.0f / 255.0f), (float)g * (1.0f / 255.0f), (float)b * (1.0f / 255.0f), (float)a* (1.0f / 255.0f)) {}
2936
0
    constexpr ImColor(ImU32 rgba)                                   : Value((float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {}
2937
0
    inline operator ImU32() const                                   { return ImGui::ColorConvertFloat4ToU32(Value); }
2938
0
    inline operator ImVec4() const                                  { return Value; }
2939
2940
    // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers.
2941
0
    inline void    SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; }
2942
0
    static ImColor HSV(float h, float s, float v, float a = 1.0f)   { float r, g, b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r, g, b, a); }
2943
};
2944
2945
//-----------------------------------------------------------------------------
2946
// [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiSelectionRequestType, ImGuiSelectionRequest, ImGuiMultiSelectIO, ImGuiSelectionBasicStorage)
2947
//-----------------------------------------------------------------------------
2948
2949
// Multi-selection system
2950
// Documentation at: https://github.com/ocornut/imgui/wiki/Multi-Select
2951
// - Refer to 'Demo->Widgets->Selection State & Multi-Select' for demos using this.
2952
// - This system implements standard multi-selection idioms (CTRL+Mouse/Keyboard, SHIFT+Mouse/Keyboard, etc)
2953
//   with support for clipper (skipping non-visible items), box-select and many other details.
2954
// - Selectable(), Checkbox() are supported but custom widgets may use it as well.
2955
// - TreeNode() is technically supported but... using this correctly is more complicated: you need some sort of linear/random access to your tree,
2956
//   which is suited to advanced trees setups also implementing filters and clipper. We will work toward simplifying and demoing it.
2957
// - In the spirit of Dear ImGui design, your code owns actual selection data.
2958
//   This is designed to allow all kinds of selection storage you may use in your application e.g. set/map/hash.
2959
// About ImGuiSelectionBasicStorage:
2960
// - This is an optional helper to store a selection state and apply selection requests.
2961
// - It is used by our demos and provided as a convenience to quickly implement multi-selection.
2962
// Usage:
2963
// - Identify submitted items with SetNextItemSelectionUserData(), most likely using an index into your current data-set.
2964
// - Store and maintain actual selection data using persistent object identifiers.
2965
// - Usage flow:
2966
//     BEGIN - (1) Call BeginMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
2967
//           - (2) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 6.
2968
//           - (3) [If using clipper] You need to make sure RangeSrcItem is always submitted. Calculate its index and pass to clipper.IncludeItemByIndex(). If storing indices in ImGuiSelectionUserData, a simple clipper.IncludeItemByIndex(ms_io->RangeSrcItem) call will work.
2969
//     LOOP  - (4) Submit your items with SetNextItemSelectionUserData() + Selectable()/TreeNode() calls.
2970
//     END   - (5) Call EndMultiSelect() and retrieve the ImGuiMultiSelectIO* result.
2971
//           - (6) Honor request list (SetAll/SetRange requests) by updating your selection data. Same code as Step 2.
2972
//     If you submit all items (no clipper), Step 2 and 3 are optional and will be handled by each item themselves. It is fine to always honor those steps.
2973
// About ImGuiSelectionUserData:
2974
// - This can store an application-defined identifier (e.g. index or pointer) submitted via SetNextItemSelectionUserData().
2975
// - In return we store them into RangeSrcItem/RangeFirstItem/RangeLastItem and other fields in ImGuiMultiSelectIO.
2976
// - Most applications will store an object INDEX, hence the chosen name and type. Storing an index is natural, because
2977
//   SetRange requests will give you two end-points and you will need to iterate/interpolate between them to update your selection.
2978
// - However it is perfectly possible to store a POINTER or another IDENTIFIER inside ImGuiSelectionUserData.
2979
//   Our system never assume that you identify items by indices, it never attempts to interpolate between two values.
2980
// - If you enable ImGuiMultiSelectFlags_NoRangeSelect then it is guaranteed that you will never have to interpolate
2981
//   between two ImGuiSelectionUserData, which may be a convenient way to use part of the feature with less code work.
2982
// - As most users will want to store an index, for convenience and to reduce confusion we use ImS64 instead of void*,
2983
//   being syntactically easier to downcast. Feel free to reinterpret_cast and store a pointer inside.
2984
2985
// Flags for BeginMultiSelect()
2986
enum ImGuiMultiSelectFlags_
2987
{
2988
    ImGuiMultiSelectFlags_None                  = 0,
2989
    ImGuiMultiSelectFlags_SingleSelect          = 1 << 0,   // Disable selecting more than one item. This is available to allow single-selection code to share same code/logic if desired. It essentially disables the main purpose of BeginMultiSelect() tho!
2990
    ImGuiMultiSelectFlags_NoSelectAll           = 1 << 1,   // Disable CTRL+A shortcut to select all.
2991
    ImGuiMultiSelectFlags_NoRangeSelect         = 1 << 2,   // Disable Shift+selection mouse/keyboard support (useful for unordered 2D selection). With BoxSelect is also ensure contiguous SetRange requests are not combined into one. This allows not handling interpolation in SetRange requests.
2992
    ImGuiMultiSelectFlags_NoAutoSelect          = 1 << 3,   // Disable selecting items when navigating (useful for e.g. supporting range-select in a list of checkboxes).
2993
    ImGuiMultiSelectFlags_NoAutoClear           = 1 << 4,   // Disable clearing selection when navigating or selecting another one (generally used with ImGuiMultiSelectFlags_NoAutoSelect. useful for e.g. supporting range-select in a list of checkboxes).
2994
    ImGuiMultiSelectFlags_NoAutoClearOnReselect = 1 << 5,   // Disable clearing selection when clicking/selecting an already selected item.
2995
    ImGuiMultiSelectFlags_BoxSelect1d           = 1 << 6,   // Enable box-selection with same width and same x pos items (e.g. full row Selectable()). Box-selection works better with little bit of spacing between items hit-box in order to be able to aim at empty space.
2996
    ImGuiMultiSelectFlags_BoxSelect2d           = 1 << 7,   // Enable box-selection with varying width or varying x pos items support (e.g. different width labels, or 2D layout/grid). This is slower: alters clipping logic so that e.g. horizontal movements will update selection of normally clipped items.
2997
    ImGuiMultiSelectFlags_BoxSelectNoScroll     = 1 << 8,   // Disable scrolling when box-selecting near edges of scope.
2998
    ImGuiMultiSelectFlags_ClearOnEscape         = 1 << 9,   // Clear selection when pressing Escape while scope is focused.
2999
    ImGuiMultiSelectFlags_ClearOnClickVoid      = 1 << 10,  // Clear selection when clicking on empty location within scope.
3000
    ImGuiMultiSelectFlags_ScopeWindow           = 1 << 11,  // Scope for _BoxSelect and _ClearOnClickVoid is whole window (Default). Use if BeginMultiSelect() covers a whole window or used a single time in same window.
3001
    ImGuiMultiSelectFlags_ScopeRect             = 1 << 12,  // Scope for _BoxSelect and _ClearOnClickVoid is rectangle encompassing BeginMultiSelect()/EndMultiSelect(). Use if BeginMultiSelect() is called multiple times in same window.
3002
    ImGuiMultiSelectFlags_SelectOnClick         = 1 << 13,  // Apply selection on mouse down when clicking on unselected item. (Default)
3003
    ImGuiMultiSelectFlags_SelectOnClickRelease  = 1 << 14,  // Apply selection on mouse release when clicking an unselected item. Allow dragging an unselected item without altering selection.
3004
    //ImGuiMultiSelectFlags_RangeSelect2d       = 1 << 15,  // Shift+Selection uses 2d geometry instead of linear sequence, so possible to use Shift+up/down to select vertically in grid. Analogous to what BoxSelect does.
3005
    ImGuiMultiSelectFlags_NavWrapX              = 1 << 16,  // [Temporary] Enable navigation wrapping on X axis. Provided as a convenience because we don't have a design for the general Nav API for this yet. When the more general feature be public we may obsolete this flag in favor of new one.
3006
};
3007
3008
// Main IO structure returned by BeginMultiSelect()/EndMultiSelect().
3009
// This mainly contains a list of selection requests.
3010
// - Use 'Demo->Tools->Debug Log->Selection' to see requests as they happen.
3011
// - Some fields are only useful if your list is dynamic and allows deletion (getting post-deletion focus/state right is shown in the demo)
3012
// - Below: who reads/writes each fields? 'r'=read, 'w'=write, 'ms'=multi-select code, 'app'=application/user code.
3013
struct ImGuiMultiSelectIO
3014
{
3015
    //------------------------------------------// BeginMultiSelect / EndMultiSelect
3016
    ImVector<ImGuiSelectionRequest> Requests;   //  ms:w, app:r     /  ms:w  app:r   // Requests to apply to your selection data.
3017
    ImGuiSelectionUserData      RangeSrcItem;   //  ms:w  app:r     /                // (If using clipper) Begin: Source item (often the first selected item) must never be clipped: use clipper.IncludeItemByIndex() to ensure it is submitted.
3018
    ImGuiSelectionUserData      NavIdItem;      //  ms:w, app:r     /                // (If using deletion) Last known SetNextItemSelectionUserData() value for NavId (if part of submitted items).
3019
    bool                        NavIdSelected;  //  ms:w, app:r     /        app:r   // (If using deletion) Last known selection state for NavId (if part of submitted items).
3020
    bool                        RangeSrcReset;  //        app:w     /  ms:r          // (If using deletion) Set before EndMultiSelect() to reset ResetSrcItem (e.g. if deleted selection).
3021
    int                         ItemsCount;     //  ms:w, app:r     /        app:r   // 'int items_count' parameter to BeginMultiSelect() is copied here for convenience, allowing simpler calls to your ApplyRequests handler. Not used internally.
3022
};
3023
3024
// Selection request type
3025
enum ImGuiSelectionRequestType
3026
{
3027
    ImGuiSelectionRequestType_None = 0,
3028
    ImGuiSelectionRequestType_SetAll,           // Request app to clear selection (if Selected==false) or select all items (if Selected==true). We cannot set RangeFirstItem/RangeLastItem as its contents is entirely up to user (not necessarily an index)
3029
    ImGuiSelectionRequestType_SetRange,         // Request app to select/unselect [RangeFirstItem..RangeLastItem] items (inclusive) based on value of Selected. Only EndMultiSelect() request this, app code can read after BeginMultiSelect() and it will always be false.
3030
};
3031
3032
// Selection request item
3033
struct ImGuiSelectionRequest
3034
{
3035
    //------------------------------------------// BeginMultiSelect / EndMultiSelect
3036
    ImGuiSelectionRequestType   Type;           //  ms:w, app:r     /  ms:w, app:r   // Request type. You'll most often receive 1 Clear + 1 SetRange with a single-item range.
3037
    bool                        Selected;       //  ms:w, app:r     /  ms:w, app:r   // Parameter for SetAll/SetRange requests (true = select, false = unselect)
3038
    ImS8                        RangeDirection; //                  /  ms:w  app:r   // Parameter for SetRange request: +1 when RangeFirstItem comes before RangeLastItem, -1 otherwise. Useful if you want to preserve selection order on a backward Shift+Click.
3039
    ImGuiSelectionUserData      RangeFirstItem; //                  /  ms:w, app:r   // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from top to bottom).
3040
    ImGuiSelectionUserData      RangeLastItem;  //                  /  ms:w, app:r   // Parameter for SetRange request (this is generally == RangeSrcItem when shift selecting from bottom to top). Inclusive!
3041
};
3042
3043
// Optional helper to store multi-selection state + apply multi-selection requests.
3044
// - Used by our demos and provided as a convenience to easily implement basic multi-selection.
3045
// - Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }'
3046
//   Or you can check 'if (Contains(id)) { ... }' for each possible object if their number is not too high to iterate.
3047
// - USING THIS IS NOT MANDATORY. This is only a helper and not a required API.
3048
// To store a multi-selection, in your application you could:
3049
// - Use this helper as a convenience. We use our simple key->value ImGuiStorage as a std::set<ImGuiID> replacement.
3050
// - Use your own external storage: e.g. std::set<MyObjectId>, std::vector<MyObjectId>, interval trees, intrusively stored selection etc.
3051
// In ImGuiSelectionBasicStorage we:
3052
// - always use indices in the multi-selection API (passed to SetNextItemSelectionUserData(), retrieved in ImGuiMultiSelectIO)
3053
// - use the AdapterIndexToStorageId() indirection layer to abstract how persistent selection data is derived from an index.
3054
// - use decently optimized logic to allow queries and insertion of very large selection sets.
3055
// - do not preserve selection order.
3056
// Many combinations are possible depending on how you prefer to store your items and how you prefer to store your selection.
3057
// Large applications are likely to eventually want to get rid of this indirection layer and do their own thing.
3058
// See https://github.com/ocornut/imgui/wiki/Multi-Select for details and pseudo-code using this helper.
3059
struct ImGuiSelectionBasicStorage
3060
{
3061
    // Members
3062
    int             Size;           //          // Number of selected items, maintained by this helper.
3063
    bool            PreserveOrder;  // = false  // GetNextSelectedItem() will return ordered selection (currently implemented by two additional sorts of selection. Could be improved)
3064
    void*           UserData;       // = NULL   // User data for use by adapter function        // e.g. selection.UserData = (void*)my_items;
3065
    ImGuiID         (*AdapterIndexToStorageId)(ImGuiSelectionBasicStorage* self, int idx);      // e.g. selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { return ((MyItems**)self->UserData)[idx]->ID; };
3066
    int             _SelectionOrder;// [Internal] Increasing counter to store selection order
3067
    ImGuiStorage    _Storage;       // [Internal] Selection set. Think of this as similar to e.g. std::set<ImGuiID>. Prefer not accessing directly: iterate with GetNextSelectedItem().
3068
3069
    // Methods
3070
    IMGUI_API ImGuiSelectionBasicStorage();
3071
    IMGUI_API void  ApplyRequests(ImGuiMultiSelectIO* ms_io);   // Apply selection requests coming from BeginMultiSelect() and EndMultiSelect() functions. It uses 'items_count' passed to BeginMultiSelect()
3072
    IMGUI_API bool  Contains(ImGuiID id) const;                 // Query if an item id is in selection.
3073
    IMGUI_API void  Clear();                                    // Clear selection
3074
    IMGUI_API void  Swap(ImGuiSelectionBasicStorage& r);        // Swap two selections
3075
    IMGUI_API void  SetItemSelected(ImGuiID id, bool selected); // Add/remove an item from selection (generally done by ApplyRequests() function)
3076
    IMGUI_API bool  GetNextSelectedItem(void** opaque_it, ImGuiID* out_id); // Iterate selection with 'void* it = NULL; ImGuiID id; while (selection.GetNextSelectedItem(&it, &id)) { ... }'
3077
0
    inline ImGuiID  GetStorageIdFromIndex(int idx)              { return AdapterIndexToStorageId(this, idx); }  // Convert index to item id based on provided adapter.
3078
};
3079
3080
// Optional helper to apply multi-selection requests to existing randomly accessible storage.
3081
// Convenient if you want to quickly wire multi-select API on e.g. an array of bool or items storing their own selection state.
3082
struct ImGuiSelectionExternalStorage
3083
{
3084
    // Members
3085
    void*           UserData;       // User data for use by adapter function                                // e.g. selection.UserData = (void*)my_items;
3086
    void            (*AdapterSetItemSelected)(ImGuiSelectionExternalStorage* self, int idx, bool selected); // e.g. AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int idx, bool selected) { ((MyItems**)self->UserData)[idx]->Selected = selected; }
3087
3088
    // Methods
3089
    IMGUI_API ImGuiSelectionExternalStorage();
3090
    IMGUI_API void  ApplyRequests(ImGuiMultiSelectIO* ms_io);   // Apply selection requests by using AdapterSetItemSelected() calls
3091
};
3092
3093
//-----------------------------------------------------------------------------
3094
// [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData)
3095
// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.
3096
//-----------------------------------------------------------------------------
3097
3098
// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking.
3099
#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX
3100
162k
#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX     (32)
3101
#endif
3102
3103
// ImDrawIdx: vertex index. [Compile-time configurable type]
3104
// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended).
3105
// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file.
3106
#ifndef ImDrawIdx
3107
typedef unsigned short ImDrawIdx;   // Default: 16-bit (for maximum compatibility with renderer backends)
3108
#endif
3109
3110
// ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h]
3111
// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering,
3112
// you can poke into the draw list for that! Draw callback may be useful for example to:
3113
//  A) Change your GPU render state,
3114
//  B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc.
3115
// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }'
3116
// If you want to override the signature of ImDrawCallback, you can simply use e.g. '#define ImDrawCallback MyDrawCallback' (in imconfig.h) + update rendering backend accordingly.
3117
#ifndef ImDrawCallback
3118
typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd);
3119
#endif
3120
3121
// Special Draw callback value to request renderer backend to reset the graphics/render state.
3122
// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address.
3123
// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored.
3124
// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call).
3125
0
#define ImDrawCallback_ResetRenderState     (ImDrawCallback)(-8)
3126
3127
// Typically, 1 command = 1 GPU draw call (unless command is a callback)
3128
// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,
3129
//   this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.
3130
//   Backends made for <1.71. will typically ignore the VtxOffset fields.
3131
// - The ClipRect/TexRef/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for).
3132
struct ImDrawCmd
3133
{
3134
    ImVec4          ClipRect;           // 4*4  // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates
3135
    ImTextureRef    TexRef;             // 16   // Reference to a font/texture atlas (where backend called ImTextureData::SetTexID()) or to a user-provided texture ID (via e.g. ImGui::Image() calls). Both will lead to a ImTextureID value.
3136
    unsigned int    VtxOffset;          // 4    // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices.
3137
    unsigned int    IdxOffset;          // 4    // Start offset in index buffer.
3138
    unsigned int    ElemCount;          // 4    // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].
3139
    ImDrawCallback  UserCallback;       // 4-8  // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.
3140
    void*           UserCallbackData;   // 4-8  // Callback user data (when UserCallback != NULL). If called AddCallback() with size == 0, this is a copy of the AddCallback() argument. If called AddCallback() with size > 0, this is pointing to a buffer where data is stored.
3141
    int             UserCallbackDataSize;  // 4 // Size of callback user data when using storage, otherwise 0.
3142
    int             UserCallbackDataOffset;// 4 // [Internal] Offset of callback user data when using storage, otherwise -1.
3143
3144
404k
    ImDrawCmd()     { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed
3145
3146
    // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)
3147
    // Since 1.92: removed ImDrawCmd::TextureId field, the getter function must be used!
3148
    inline ImTextureID GetTexID() const;    // == (TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID)
3149
};
3150
3151
// Vertex layout
3152
#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT
3153
struct ImDrawVert
3154
{
3155
    ImVec2  pos;
3156
    ImVec2  uv;
3157
    ImU32   col;
3158
};
3159
#else
3160
// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h
3161
// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine.
3162
// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared at the time you'd want to set your type up.
3163
// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM.
3164
IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT;
3165
#endif
3166
3167
// [Internal] For use by ImDrawList
3168
struct ImDrawCmdHeader
3169
{
3170
    ImVec4          ClipRect;
3171
    ImTextureRef    TexRef;
3172
    unsigned int    VtxOffset;
3173
};
3174
3175
// [Internal] For use by ImDrawListSplitter
3176
struct ImDrawChannel
3177
{
3178
    ImVector<ImDrawCmd>         _CmdBuffer;
3179
    ImVector<ImDrawIdx>         _IdxBuffer;
3180
};
3181
3182
// Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order.
3183
// This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call.
3184
struct ImDrawListSplitter
3185
{
3186
    int                         _Current;    // Current channel number (0)
3187
    int                         _Count;      // Number of active channels (1+)
3188
    ImVector<ImDrawChannel>     _Channels;   // Draw channels (not resized down so _Count might be < Channels.Size)
3189
3190
3
    inline ImDrawListSplitter()  { memset(this, 0, sizeof(*this)); }
3191
3
    inline ~ImDrawListSplitter() { ClearFreeMemory(); }
3192
161k
    inline void                 Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame
3193
    IMGUI_API void              ClearFreeMemory();
3194
    IMGUI_API void              Split(ImDrawList* draw_list, int count);
3195
    IMGUI_API void              Merge(ImDrawList* draw_list);
3196
    IMGUI_API void              SetCurrentChannel(ImDrawList* draw_list, int channel_idx);
3197
};
3198
3199
// Flags for ImDrawList functions
3200
// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused)
3201
enum ImDrawFlags_
3202
{
3203
    ImDrawFlags_None                        = 0,
3204
    ImDrawFlags_Closed                      = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason)
3205
    ImDrawFlags_RoundCornersTopLeft         = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01.
3206
    ImDrawFlags_RoundCornersTopRight        = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02.
3207
    ImDrawFlags_RoundCornersBottomLeft      = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04.
3208
    ImDrawFlags_RoundCornersBottomRight     = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08.
3209
    ImDrawFlags_RoundCornersNone            = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag!
3210
    ImDrawFlags_RoundCornersTop             = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight,
3211
    ImDrawFlags_RoundCornersBottom          = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,
3212
    ImDrawFlags_RoundCornersLeft            = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft,
3213
    ImDrawFlags_RoundCornersRight           = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight,
3214
    ImDrawFlags_RoundCornersAll             = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,
3215
    ImDrawFlags_RoundCornersDefault_        = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified.
3216
    ImDrawFlags_RoundCornersMask_           = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone,
3217
};
3218
3219
// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.
3220
// It is however possible to temporarily alter flags between calls to ImDrawList:: functions.
3221
enum ImDrawListFlags_
3222
{
3223
    ImDrawListFlags_None                    = 0,
3224
    ImDrawListFlags_AntiAliasedLines        = 1 << 0,  // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)
3225
    ImDrawListFlags_AntiAliasedLinesUseTex  = 1 << 1,  // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).
3226
    ImDrawListFlags_AntiAliasedFill         = 1 << 2,  // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).
3227
    ImDrawListFlags_AllowVtxOffset          = 1 << 3,  // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.
3228
};
3229
3230
// Draw command list
3231
// This is the low-level list of polygons that ImGui:: functions are filling. At the end of the frame,
3232
// all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering.
3233
// Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to
3234
// access the current window draw list and draw custom primitives.
3235
// You can interleave normal ImGui:: calls and adding primitives to the current draw list.
3236
// In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize).
3237
// You are totally free to apply whatever transformation matrix you want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!)
3238
// Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects.
3239
struct ImDrawList
3240
{
3241
    // This is what you have to render
3242
    ImVector<ImDrawCmd>     CmdBuffer;          // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback.
3243
    ImVector<ImDrawIdx>     IdxBuffer;          // Index buffer. Each command consume ImDrawCmd::ElemCount of those
3244
    ImVector<ImDrawVert>    VtxBuffer;          // Vertex buffer.
3245
    ImDrawListFlags         Flags;              // Flags, you may poke into these to adjust anti-aliasing settings per-primitive.
3246
3247
    // [Internal, used while building lists]
3248
    unsigned int            _VtxCurrentIdx;     // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.
3249
    ImDrawListSharedData*   _Data;              // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)
3250
    ImDrawVert*             _VtxWritePtr;       // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
3251
    ImDrawIdx*              _IdxWritePtr;       // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)
3252
    ImVector<ImVec2>        _Path;              // [Internal] current path building
3253
    ImDrawCmdHeader         _CmdHeader;         // [Internal] template of active commands. Fields should match those of CmdBuffer.back().
3254
    ImDrawListSplitter      _Splitter;          // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)
3255
    ImVector<ImVec4>        _ClipRectStack;     // [Internal]
3256
    ImVector<ImTextureRef>  _TextureStack;      // [Internal]
3257
    ImVector<ImU8>          _CallbacksDataBuf;  // [Internal]
3258
    float                   _FringeScale;       // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content
3259
    const char*             _OwnerName;         // Pointer to owner window's name for debugging
3260
3261
    // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData().
3262
    // (advanced: you may create and use your own ImDrawListSharedData so you can use ImDrawList without ImGui, but that's more involved)
3263
    IMGUI_API ImDrawList(ImDrawListSharedData* shared_data);
3264
    IMGUI_API ~ImDrawList();
3265
3266
    IMGUI_API void  PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false);  // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
3267
    IMGUI_API void  PushClipRectFullScreen();
3268
    IMGUI_API void  PopClipRect();
3269
    IMGUI_API void  PushTexture(ImTextureRef tex_ref);
3270
    IMGUI_API void  PopTexture();
3271
0
    inline ImVec2   GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); }
3272
0
    inline ImVec2   GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); }
3273
3274
    // Primitives
3275
    // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
3276
    // - For rectangular primitives, "p_min" and "p_max" represent the upper-left and lower-right corners.
3277
    // - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred).
3278
    //   In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12.
3279
    //   In future versions we will use textures to provide cheaper and higher-quality circles.
3280
    //   Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides.
3281
    IMGUI_API void  AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f);
3282
    IMGUI_API void  AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);   // a: upper-left, b: lower-right (== upper-left + size)
3283
    IMGUI_API void  AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0);                     // a: upper-left, b: lower-right (== upper-left + size)
3284
    IMGUI_API void  AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left);
3285
    IMGUI_API void  AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f);
3286
    IMGUI_API void  AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col);
3287
    IMGUI_API void  AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f);
3288
    IMGUI_API void  AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col);
3289
    IMGUI_API void  AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f);
3290
    IMGUI_API void  AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0);
3291
    IMGUI_API void  AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f);
3292
    IMGUI_API void  AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);
3293
    IMGUI_API void  AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);
3294
    IMGUI_API void  AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot = 0.0f, int num_segments = 0);
3295
    IMGUI_API void  AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);
3296
    IMGUI_API void  AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);
3297
    IMGUI_API void  AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)
3298
    IMGUI_API void  AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0);               // Quadratic Bezier (3 control points)
3299
3300
    // General polygon
3301
    // - Only simple polygons are supported by filling functions (no self-intersections, no holes).
3302
    // - Concave polygon fill is more expensive than convex one: it has O(N^2) complexity. Provided as a convenience for the user but not used by the main library.
3303
    IMGUI_API void  AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);
3304
    IMGUI_API void  AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);
3305
    IMGUI_API void  AddConcavePolyFilled(const ImVec2* points, int num_points, ImU32 col);
3306
3307
    // Image primitives
3308
    // - Read FAQ to understand what ImTextureID/ImTextureRef are.
3309
    // - "p_min" and "p_max" represent the upper-left and lower-right corners of the rectangle.
3310
    // - "uv_min" and "uv_max" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture.
3311
    IMGUI_API void  AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE);
3312
    IMGUI_API void  AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE);
3313
    IMGUI_API void  AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);
3314
3315
    // Stateful path API, add points then finish with PathFillConvex() or PathStroke()
3316
    // - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
3317
    //   so e.g. 'PathArcTo(center, radius, PI * -0.5f, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5f)' won't have correct anti-aliasing when followed by PathFillConvex().
3318
0
    inline    void  PathClear()                                                 { _Path.Size = 0; }
3319
891k
    inline    void  PathLineTo(const ImVec2& pos)                               { _Path.push_back(pos); }
3320
0
    inline    void  PathLineToMergeDuplicate(const ImVec2& pos)                 { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }
3321
80.5k
    inline    void  PathFillConvex(ImU32 col)                                   { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
3322
0
    inline    void  PathFillConcave(ImU32 col)                                  { AddConcavePolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }
3323
162k
    inline    void  PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; }
3324
    IMGUI_API void  PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);
3325
    IMGUI_API void  PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12);                // Use precomputed angles for a 12 steps circle
3326
    IMGUI_API void  PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse
3327
    IMGUI_API void  PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points)
3328
    IMGUI_API void  PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0);               // Quadratic Bezier (3 control points)
3329
    IMGUI_API void  PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0);
3330
3331
    // Advanced: Draw Callbacks
3332
    // - May be used to alter render state (change sampler, blending, current shader). May be used to emit custom rendering commands (difficult to do correctly, but possible).
3333
    // - Use special ImDrawCallback_ResetRenderState callback to instruct backend to reset its render state to the default.
3334
    // - Your rendering loop must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. All standard backends are honoring this.
3335
    // - For some backends, the callback may access selected render-states exposed by the backend in a ImGui_ImplXXXX_RenderState structure pointed to by platform_io.Renderer_RenderState.
3336
    // - IMPORTANT: please be mindful of the different level of indirection between using size==0 (copying argument) and using size>0 (copying pointed data into a buffer).
3337
    //   - If userdata_size == 0: we copy/store the 'userdata' argument as-is. It will be available unmodified in ImDrawCmd::UserCallbackData during render.
3338
    //   - If userdata_size > 0,  we copy/store 'userdata_size' bytes pointed to by 'userdata'. We store them in a buffer stored inside the drawlist. ImDrawCmd::UserCallbackData will point inside that buffer so you have to retrieve data from there. Your callback may need to use ImDrawCmd::UserCallbackDataSize if you expect dynamically-sized data.
3339
    //   - Support for userdata_size > 0 was added in v1.91.4, October 2024. So earlier code always only allowed to copy/store a simple void*.
3340
    IMGUI_API void  AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size = 0);
3341
3342
    // Advanced: Miscellaneous
3343
    IMGUI_API void  AddDrawCmd();                                               // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible
3344
    IMGUI_API ImDrawList* CloneOutput() const;                                  // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club instead.
3345
3346
    // Advanced: Channels
3347
    // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)
3348
    // - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end)
3349
    // - This API shouldn't have been in ImDrawList in the first place!
3350
    //   Prefer using your own persistent instance of ImDrawListSplitter as you can stack them.
3351
    //   Using the ImDrawList::ChannelsXXXX you cannot stack a split over another.
3352
0
    inline void     ChannelsSplit(int count)    { _Splitter.Split(this, count); }
3353
0
    inline void     ChannelsMerge()             { _Splitter.Merge(this); }
3354
0
    inline void     ChannelsSetCurrent(int n)   { _Splitter.SetCurrentChannel(this, n); }
3355
3356
    // Advanced: Primitives allocations
3357
    // - We render triangles (three vertices)
3358
    // - All primitives needs to be reserved via PrimReserve() beforehand.
3359
    IMGUI_API void  PrimReserve(int idx_count, int vtx_count);
3360
    IMGUI_API void  PrimUnreserve(int idx_count, int vtx_count);
3361
    IMGUI_API void  PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col);      // Axis aligned rectangle (composed of two triangles)
3362
    IMGUI_API void  PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col);
3363
    IMGUI_API void  PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col);
3364
0
    inline    void  PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)    { _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; }
3365
0
    inline    void  PrimWriteIdx(ImDrawIdx idx)                                     { *_IdxWritePtr = idx; _IdxWritePtr++; }
3366
0
    inline    void  PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)         { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index
3367
3368
    // Obsolete names
3369
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3370
0
    inline    void  PushTextureID(ImTextureRef tex_ref) { PushTexture(tex_ref); }   // RENAMED in 1.92.x
3371
0
    inline    void  PopTextureID()                      { PopTexture(); }           // RENAMED in 1.92.x
3372
#endif
3373
    //inline  void  AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f) { AddEllipse(center, ImVec2(radius_x, radius_y), col, rot, num_segments, thickness); } // OBSOLETED in 1.90.5 (Mar 2024)
3374
    //inline  void  AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0) { AddEllipseFilled(center, ImVec2(radius_x, radius_y), col, rot, num_segments); }                        // OBSOLETED in 1.90.5 (Mar 2024)
3375
    //inline  void  PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0) { PathEllipticalArcTo(center, ImVec2(radius_x, radius_y), rot, a_min, a_max, num_segments); } // OBSOLETED in 1.90.5 (Mar 2024)
3376
    //inline  void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); }                         // OBSOLETED in 1.80 (Jan 2021)
3377
    //inline  void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); }                                                                                // OBSOLETED in 1.80 (Jan 2021)
3378
3379
    // [Internal helpers]
3380
    IMGUI_API void  _SetDrawListSharedData(ImDrawListSharedData* data);
3381
    IMGUI_API void  _ResetForNewFrame();
3382
    IMGUI_API void  _ClearFreeMemory();
3383
    IMGUI_API void  _PopUnusedDrawCmd();
3384
    IMGUI_API void  _TryMergeDrawCmds();
3385
    IMGUI_API void  _OnChangedClipRect();
3386
    IMGUI_API void  _OnChangedTexture();
3387
    IMGUI_API void  _OnChangedVtxOffset();
3388
    IMGUI_API void  _SetTexture(ImTextureRef tex_ref);
3389
    IMGUI_API int   _CalcCircleAutoSegmentCount(float radius) const;
3390
    IMGUI_API void  _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step);
3391
    IMGUI_API void  _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments);
3392
};
3393
3394
// All draw data to render a Dear ImGui frame
3395
// (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose,
3396
// as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList)
3397
struct ImDrawData
3398
{
3399
    bool                Valid;              // Only valid after Render() is called and before the next NewFrame() is called.
3400
    int                 CmdListsCount;      // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render.
3401
    int                 TotalIdxCount;      // For convenience, sum of all ImDrawList's IdxBuffer.Size
3402
    int                 TotalVtxCount;      // For convenience, sum of all ImDrawList's VtxBuffer.Size
3403
    ImVector<ImDrawList*> CmdLists;         // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here.
3404
    ImVec2              DisplayPos;         // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)
3405
    ImVec2              DisplaySize;        // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)
3406
    ImVec2              FramebufferScale;   // Amount of pixels for each unit of DisplaySize. Copied from viewport->FramebufferScale (== io.DisplayFramebufferScale for main viewport). Generally (1,1) on normal display, (2,2) on OSX with Retina display.
3407
    ImGuiViewport*      OwnerViewport;      // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not).
3408
    ImVector<ImTextureData*>* Textures;     // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overriden or set to NULL if you want to manually update textures.
3409
3410
    // Functions
3411
1
    ImDrawData()    { Clear(); }
3412
    IMGUI_API void  Clear();
3413
    IMGUI_API void  AddDrawList(ImDrawList* draw_list);     // Helper to add an external draw list into an existing ImDrawData.
3414
    IMGUI_API void  DeIndexAllBuffers();                    // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
3415
    IMGUI_API void  ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution.
3416
};
3417
3418
//-----------------------------------------------------------------------------
3419
// [SECTION] Texture API (ImTextureFormat, ImTextureStatus, ImTextureRect, ImTextureData)
3420
//-----------------------------------------------------------------------------
3421
// In principle, the only data types that user/application code should care about are 'ImTextureRef' and 'ImTextureID'.
3422
// They are defined above in this header file. Read their description to the difference between ImTextureRef and ImTextureID.
3423
// FOR ALL OTHER ImTextureXXXX TYPES: ONLY CORE LIBRARY AND RENDERER BACKENDS NEED TO KNOW AND CARE ABOUT THEM.
3424
//-----------------------------------------------------------------------------
3425
3426
#undef Status // X11 headers are leaking this.
3427
3428
// We intentionally support a limited amount of texture formats to limit burden on CPU-side code and extension.
3429
// Most standard backends only support RGBA32 but we provide a single channel option for low-resource/embedded systems.
3430
enum ImTextureFormat
3431
{
3432
    ImTextureFormat_RGBA32,         // 4 components per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight * 4
3433
    ImTextureFormat_Alpha8,         // 1 component per pixel, each is unsigned 8-bit. Total size = TexWidth * TexHeight
3434
};
3435
3436
// Status of a texture to communicate with Renderer Backend.
3437
enum ImTextureStatus
3438
{
3439
    ImTextureStatus_OK,
3440
    ImTextureStatus_Destroyed,      // Backend destroyed the texture.
3441
    ImTextureStatus_WantCreate,     // Requesting backend to create the texture. Set status OK when done.
3442
    ImTextureStatus_WantUpdates,    // Requesting backend to update specific blocks of pixels (write to texture portions which have never been used before). Set status OK when done.
3443
    ImTextureStatus_WantDestroy,    // Requesting backend to destroy the texture. Set status to Destroyed when done.
3444
};
3445
3446
// Coordinates of a rectangle within a texture.
3447
// When a texture is in ImTextureStatus_WantUpdates state, we provide a list of individual rectangles to copy to the graphics system.
3448
// You may use ImTextureData::Updates[] for the list, or ImTextureData::UpdateBox for a single bounding box.
3449
struct ImTextureRect
3450
{
3451
    unsigned short      x, y;       // Upper-left coordinates of rectangle to update
3452
    unsigned short      w, h;       // Size of rectangle to update (in pixels)
3453
};
3454
3455
// Specs and pixel storage for a texture used by Dear ImGui.
3456
// This is only useful for (1) core library and (2) backends. End-user/applications do not need to care about this.
3457
// Renderer Backends will create a GPU-side version of this.
3458
// Why does we store two identifiers: TexID and BackendUserData?
3459
// - ImTextureID    TexID           = lower-level identifier stored in ImDrawCmd. ImDrawCmd can refer to textures not created by the backend, and for which there's no ImTextureData.
3460
// - void*          BackendUserData = higher-level opaque storage for backend own book-keeping. Some backends may have enough with TexID and not need both.
3461
 // In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend
3462
struct ImTextureData
3463
{
3464
    //------------------------------------------ core / backend ---------------------------------------
3465
    int                 UniqueID;               // w    -   // [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas.
3466
    ImTextureStatus     Status;                 // rw   rw  // ImTextureStatus_OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify!
3467
    void*               BackendUserData;        // -    rw  // Convenience storage for backend. Some backends may have enough with TexID.
3468
    ImTextureID         TexID;                  // r    w   // Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function.
3469
    ImTextureFormat     Format;                 // w    r   // ImTextureFormat_RGBA32 (default) or ImTextureFormat_Alpha8
3470
    int                 Width;                  // w    r   // Texture width
3471
    int                 Height;                 // w    r   // Texture height
3472
    int                 BytesPerPixel;          // w    r   // 4 or 1
3473
    unsigned char*      Pixels;                 // w    r   // Pointer to buffer holding 'Width*Height' pixels and 'Width*Height*BytesPerPixels' bytes.
3474
    ImTextureRect       UsedRect;               // w    r   // Bounding box encompassing all past and queued Updates[].
3475
    ImTextureRect       UpdateRect;             // w    r   // Bounding box encompassing all queued Updates[].
3476
    ImVector<ImTextureRect> Updates;            // w    r   // Array of individual updates.
3477
    int                 UnusedFrames;           // w    r   // In order to facilitate handling Status==WantDestroy in some backend: this is a count successive frames where the texture was not used. Always >0 when Status==WantDestroy.
3478
    unsigned short      RefCount;               // w    r   // Number of contexts using this texture. Used during backend shutdown.
3479
    bool                UseColors;              // w    r   // Tell whether our texture data is known to use colors (rather than just white + alpha).
3480
    bool                WantDestroyNextFrame;   // rw   -   // [Internal] Queued to set ImTextureStatus_WantDestroy next frame. May still be used in the current frame.
3481
3482
    // Functions
3483
1
    ImTextureData()     { memset(this, 0, sizeof(*this)); Status = ImTextureStatus_Destroyed; TexID = ImTextureID_Invalid; }
3484
1
    ~ImTextureData()    { DestroyPixels(); }
3485
    IMGUI_API void      Create(ImTextureFormat format, int w, int h);
3486
    IMGUI_API void      DestroyPixels();
3487
1
    void*               GetPixels()                 { IM_ASSERT(Pixels != NULL); return Pixels; }
3488
79
    void*               GetPixelsAt(int x, int y)   { IM_ASSERT(Pixels != NULL); return Pixels + (x + y * Width) * BytesPerPixel; }
3489
0
    int                 GetSizeInBytes() const      { return Width * Height * BytesPerPixel; }
3490
45
    int                 GetPitch() const            { return Width * BytesPerPixel; }
3491
0
    ImTextureRef        GetTexRef()                 { ImTextureRef tex_ref; tex_ref._TexData = this; tex_ref._TexID = ImTextureID_Invalid; return tex_ref; }
3492
0
    ImTextureID         GetTexID() const            { return TexID; }
3493
3494
    // Called by Renderer backend
3495
    // - Call SetTexID() and SetStatus() after honoring texture requests. Never modify TexID and Status directly!
3496
    // - A backend may decide to destroy a texture that we did not request to destroy, which is fine (e.g. freeing resources), but we immediately set the texture back in _WantCreate mode.
3497
2
    void    SetTexID(ImTextureID tex_id)            { TexID = tex_id; }
3498
4
    void    SetStatus(ImTextureStatus status)       { Status = status; if (status == ImTextureStatus_Destroyed && !WantDestroyNextFrame) Status = ImTextureStatus_WantCreate; }
3499
};
3500
3501
//-----------------------------------------------------------------------------
3502
// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont)
3503
//-----------------------------------------------------------------------------
3504
3505
// A font input/source (we may rename this to ImFontSource in the future)
3506
struct ImFontConfig
3507
{
3508
    // Data Source
3509
    char            Name[40];               // <auto>   // Name (strictly to ease debugging, hence limited size buffer)
3510
    void*           FontData;               //          // TTF/OTF data
3511
    int             FontDataSize;           //          // TTF/OTF data size
3512
    bool            FontDataOwnedByAtlas;   // true     // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
3513
3514
    // Options
3515
    bool            MergeMode;              // false    // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
3516
    bool            PixelSnapH;             // false    // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.
3517
    bool            PixelSnapV;             // true     // Align Scaled GlyphOffset.y to pixel boundaries.
3518
    ImS8            OversampleH;            // 0 (2)    // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
3519
    ImS8            OversampleV;            // 0 (1)    // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis.
3520
    ImWchar         EllipsisChar;           // 0        // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
3521
    float           SizePixels;             //          // Size in pixels for rasterizer (more or less maps to the resulting font height).
3522
    const ImWchar*  GlyphRanges;            // NULL     // *LEGACY* THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list).
3523
    const ImWchar*  GlyphExcludeRanges;     // NULL     // Pointer to a small user-provided list of Unicode ranges (2 value per range, values are inclusive, zero-terminated list). This is very close to GlyphRanges[] but designed to exclude ranges from a font source, when merging fonts with overlapping glyphs. Use "Input Glyphs Overlap Detection Tool" to find about your overlapping ranges.
3524
    //ImVec2        GlyphExtraSpacing;      // 0, 0     // (REMOVED AT IT SEEMS LARGELY OBSOLETE. PLEASE REPORT IF YOU WERE USING THIS). Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now.
3525
    ImVec2          GlyphOffset;            // 0, 0     // Offset (in pixels) all glyphs from this font input. Absolute value for default size, other sizes will scale this value.
3526
    float           GlyphMinAdvanceX;       // 0        // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font. Absolute value for default size, other sizes will scale this value.
3527
    float           GlyphMaxAdvanceX;       // FLT_MAX  // Maximum AdvanceX for glyphs
3528
    float           GlyphExtraAdvanceX;     // 0        // Extra spacing (in pixels) between glyphs. Please contact us if you are using this. // FIXME-NEWATLAS: Intentionally unscaled
3529
    ImU32           FontNo;                 // 0        // Index of font within TTF/OTF file
3530
    unsigned int    FontLoaderFlags;        // 0        // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.
3531
    //unsigned int  FontBuilderFlags;       // --       // [Renamed in 1.92] Ue FontLoaderFlags.
3532
    float           RasterizerMultiply;     // 1.0f     // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.
3533
    float           RasterizerDensity;      // 1.0f     // [LEGACY: this only makes sense when ImGuiBackendFlags_RendererHasTextures is not supported] DPI scale multiplier for rasterization. Not altering other font metrics: makes it easy to swap between e.g. a 100% and a 400% fonts for a zooming display, or handle Retina screen. IMPORTANT: If you change this it is expected that you increase/decrease font scale roughly to the inverse of this, otherwise quality may look lowered.
3534
3535
    // [Internal]
3536
    ImFontFlags     Flags;                  // Font flags (don't use just yet, will be exposed in upcoming 1.92.X updates)
3537
    ImFont*         DstFont;                // Target font (as we merging fonts, multiple ImFontConfig may target the same font)
3538
    const ImFontLoader* FontLoader;         // Custom font backend for this source (default source is the one stored in ImFontAtlas)
3539
    void*           FontLoaderData;         // Font loader opaque storage (per font config)
3540
3541
    IMGUI_API ImFontConfig();
3542
};
3543
3544
// Hold rendering data for one glyph.
3545
// (Note: some language parsers may fail to convert the bitfield members, in this case maybe drop store a single u32 or we can rework this)
3546
struct ImFontGlyph
3547
{
3548
    unsigned int    Colored : 1;        // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops)
3549
    unsigned int    Visible : 1;        // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering.
3550
    unsigned int    SourceIdx : 4;      // Index of source in parent font
3551
    unsigned int    Codepoint : 26;     // 0x0000..0x10FFFF
3552
    float           AdvanceX;           // Horizontal distance to advance cursor/layout position.
3553
    float           X0, Y0, X1, Y1;     // Glyph corners. Offsets from current cursor/layout position.
3554
    float           U0, V0, U1, V1;     // Texture coordinates for the current value of ImFontAtlas->TexRef. Cached equivalent of calling GetCustomRect() with PackId.
3555
    int             PackId;             // [Internal] ImFontAtlasRectId value (FIXME: Cold data, could be moved elsewhere?)
3556
3557
22
    ImFontGlyph()   { memset(this, 0, sizeof(*this)); PackId = -1; }
3558
};
3559
3560
// Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges().
3561
// This is essentially a tightly packed of vector of 64k booleans = 8KB storage.
3562
struct ImFontGlyphRangesBuilder
3563
{
3564
    ImVector<ImU32> UsedChars;            // Store 1-bit per Unicode code point (0=unused, 1=used)
3565
3566
0
    ImFontGlyphRangesBuilder()              { Clear(); }
3567
0
    inline void     Clear()                 { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX + 1) / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); }
3568
0
    inline bool     GetBit(size_t n) const  { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; }  // Get bit n in the array
3569
0
    inline void     SetBit(size_t n)        { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; }               // Set bit n in the array
3570
0
    inline void     AddChar(ImWchar c)      { SetBit(c); }                      // Add character
3571
    IMGUI_API void  AddText(const char* text, const char* text_end = NULL);     // Add string (each character of the UTF-8 string are added)
3572
    IMGUI_API void  AddRanges(const ImWchar* ranges);                           // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext
3573
    IMGUI_API void  BuildRanges(ImVector<ImWchar>* out_ranges);                 // Output new ranges
3574
};
3575
3576
// An opaque identifier to a rectangle in the atlas. -1 when invalid.
3577
// The rectangle may move and UV may be invalidated, use GetCustomRect() to retrieve it.
3578
typedef int ImFontAtlasRectId;
3579
70
#define ImFontAtlasRectId_Invalid -1
3580
3581
// Output of ImFontAtlas::GetCustomRect() when using custom rectangles.
3582
// Those values may not be cached/stored as they are only valid for the current value of atlas->TexRef
3583
// (this is in theory derived from ImTextureRect but we use separate structures for reasons)
3584
struct ImFontAtlasRect
3585
{
3586
    unsigned short  x, y;               // Position (in current texture)
3587
    unsigned short  w, h;               // Size
3588
    ImVec2          uv0, uv1;           // UV coordinates (in current texture)
3589
3590
3
    ImFontAtlasRect() { memset(this, 0, sizeof(*this)); }
3591
};
3592
3593
// Flags for ImFontAtlas build
3594
enum ImFontAtlasFlags_
3595
{
3596
    ImFontAtlasFlags_None               = 0,
3597
    ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0,   // Don't round the height to next power of two
3598
    ImFontAtlasFlags_NoMouseCursors     = 1 << 1,   // Don't build software mouse cursors into the atlas (save a little texture memory)
3599
    ImFontAtlasFlags_NoBakedLines       = 1 << 2,   // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).
3600
};
3601
3602
// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:
3603
//  - One or more fonts.
3604
//  - Custom graphics data needed to render the shapes needed by Dear ImGui.
3605
//  - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas).
3606
//  - If you don't call any AddFont*** functions, the default font embedded in the code will be loaded for you.
3607
// It is the rendering backend responsibility to upload texture into your graphics API:
3608
//  - ImGui_ImplXXXX_RenderDrawData() functions generally iterate platform_io->Textures[] to create/update/destroy each ImTextureData instance.
3609
//  - Backend then set ImTextureData's TexID and BackendUserData.
3610
//  - Texture id are passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID/ImTextureRef for more details.
3611
// Legacy path:
3612
//  - Call Build() + GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data.
3613
//  - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API.
3614
// Common pitfalls:
3615
// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the
3616
//   atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data.
3617
// - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction.
3618
//   You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed,
3619
// - Even though many functions are suffixed with "TTF", OTF data is supported just as well.
3620
// - This is an old API and it is currently awkward for those and various other reasons! We will address them in the future!
3621
struct ImFontAtlas
3622
{
3623
    IMGUI_API ImFontAtlas();
3624
    IMGUI_API ~ImFontAtlas();
3625
    IMGUI_API ImFont*           AddFont(const ImFontConfig* font_cfg);
3626
    IMGUI_API ImFont*           AddFontDefault(const ImFontConfig* font_cfg = NULL);
3627
    IMGUI_API ImFont*           AddFontFromFileTTF(const char* filename, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);
3628
    IMGUI_API ImFont*           AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.
3629
    IMGUI_API ImFont*           AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.
3630
    IMGUI_API ImFont*           AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels = 0.0f, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);              // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.
3631
    IMGUI_API void              RemoveFont(ImFont* font);
3632
3633
    IMGUI_API void              Clear();                    // Clear everything (input fonts, output glyphs/textures)
3634
    IMGUI_API void              CompactCache();             // Compact cached glyphs and texture.
3635
    IMGUI_API void              SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime.
3636
3637
    // As we are transitioning toward a new font system, we expect to obsolete those soon:
3638
    IMGUI_API void              ClearInputData();           // [OBSOLETE] Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.
3639
    IMGUI_API void              ClearFonts();               // [OBSOLETE] Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates).
3640
    IMGUI_API void              ClearTexData();             // [OBSOLETE] Clear CPU-side copy of the texture data. Saves RAM once the texture has been copied to graphics memory.
3641
3642
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3643
    // Legacy path for build atlas + retrieving pixel data.
3644
    // - User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().
3645
    // - The pitch is always = Width * BytesPerPixels (1 or 4)
3646
    // - Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into
3647
    //   the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste).
3648
    // - From 1.92 with backends supporting ImGuiBackendFlags_RendererHasTextures:
3649
    //   - Calling Build(), GetTexDataAsAlpha8(), GetTexDataAsRGBA32() is not needed.
3650
    //   - In backend: replace calls to ImFontAtlas::SetTexID() with calls to ImTextureData::SetTexID() after honoring texture creation.
3651
    IMGUI_API bool  Build();                    // Build pixels data. This is called automatically for you by the GetTexData*** functions.
3652
    IMGUI_API void  GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel
3653
    IMGUI_API void  GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel
3654
0
    void            SetTexID(ImTextureID id)    { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid); TexRef._TexData->TexID = id; }                               // Called by legacy backends. May be called before texture creation.
3655
0
    void            SetTexID(ImTextureRef id)   { IM_ASSERT(TexRef._TexID == ImTextureID_Invalid && id._TexData == NULL); TexRef._TexData->TexID = id._TexID; } // Called by legacy backends.
3656
0
    bool            IsBuilt() const { return Fonts.Size > 0 && TexIsBuilt; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent..
3657
#endif
3658
3659
    //-------------------------------------------
3660
    // Glyph Ranges
3661
    //-------------------------------------------
3662
3663
    // Since 1.92: specifying glyph ranges is only useful/necessary if your backend doesn't support ImGuiBackendFlags_RendererHasTextures!
3664
    IMGUI_API const ImWchar*    GetGlyphRangesDefault();                // Basic Latin, Extended Latin
3665
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3666
    // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list)
3667
    // NB: Make sure that your string are UTF-8 and NOT in your local code page.
3668
    // Read https://github.com/ocornut/imgui/blob/master/docs/FONTS.md/#about-utf-8-encoding for details.
3669
    // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data.
3670
    IMGUI_API const ImWchar*    GetGlyphRangesGreek();                  // Default + Greek and Coptic
3671
    IMGUI_API const ImWchar*    GetGlyphRangesKorean();                 // Default + Korean characters
3672
    IMGUI_API const ImWchar*    GetGlyphRangesJapanese();               // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs
3673
    IMGUI_API const ImWchar*    GetGlyphRangesChineseFull();            // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
3674
    IMGUI_API const ImWchar*    GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese
3675
    IMGUI_API const ImWchar*    GetGlyphRangesCyrillic();               // Default + about 400 Cyrillic characters
3676
    IMGUI_API const ImWchar*    GetGlyphRangesThai();                   // Default + Thai characters
3677
    IMGUI_API const ImWchar*    GetGlyphRangesVietnamese();             // Default + Vietnamese characters
3678
#endif
3679
3680
    //-------------------------------------------
3681
    // [ALPHA] Custom Rectangles/Glyphs API
3682
    //-------------------------------------------
3683
3684
    // Register and retrieve custom rectangles
3685
    // - You can request arbitrary rectangles to be packed into the atlas, for your own purpose.
3686
    // - Since 1.92.X, packing is done immediately in the function call (previously packing was done during the Build call)
3687
    // - You can render your pixels into the texture right after calling the AddCustomRect() functions.
3688
    // - VERY IMPORTANT:
3689
    //   - Texture may be created/resized at any time when calling ImGui or ImFontAtlas functions.
3690
    //   - IT WILL INVALIDATE RECTANGLE DATA SUCH AS UV COORDINATES. Always use latest values from GetCustomRect().
3691
    //   - UV coordinates are associated to the current texture identifier aka 'atlas->TexRef'. Both TexRef and UV coordinates are typically changed at the same time.
3692
    // - If you render colored output into your custom rectangles: set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of preferred texture format.
3693
    // - Read docs/FONTS.md for more details about using colorful icons.
3694
    // - Note: this API may be reworked further in order to facilitate supporting e.g. multi-monitor, varying DPI settings.
3695
    // - (Pre-1.92 names) ------------> (1.92 names)
3696
    //   - GetCustomRectByIndex()   --> Use GetCustomRect()
3697
    //   - CalcCustomRectUV()       --> Use GetCustomRect() and read uv0, uv1 fields.
3698
    //   - AddCustomRectRegular()   --> Renamed to AddCustomRect()
3699
    //   - AddCustomRectFontGlyph() --> Prefer using custom ImFontLoader inside ImFontConfig
3700
    //   - ImFontAtlasCustomRect    --> Renamed to ImFontAtlasRect
3701
    IMGUI_API ImFontAtlasRectId AddCustomRect(int width, int height, ImFontAtlasRect* out_r = NULL);// Register a rectangle. Return -1 (ImFontAtlasRectId_Invalid) on error.
3702
    IMGUI_API void              RemoveCustomRect(ImFontAtlasRectId id);                             // Unregister a rectangle. Existing pixels will stay in texture until resized / garbage collected.
3703
    IMGUI_API bool              GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const;  // Get rectangle coordinates for current texture. Valid immediately, never store this (read above)!
3704
3705
    //-------------------------------------------
3706
    // Members
3707
    //-------------------------------------------
3708
3709
    // Input
3710
    ImFontAtlasFlags            Flags;              // Build flags (see ImFontAtlasFlags_)
3711
    ImTextureFormat             TexDesiredFormat;   // Desired texture format (default to ImTextureFormat_RGBA32 but may be changed to ImTextureFormat_Alpha8).
3712
    int                         TexGlyphPadding;    // FIXME: Should be called "TexPackPadding". Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false).
3713
    int                         TexMinWidth;        // Minimum desired texture width. Must be a power of two. Default to 512.
3714
    int                         TexMinHeight;       // Minimum desired texture height. Must be a power of two. Default to 128.
3715
    int                         TexMaxWidth;        // Maximum desired texture width. Must be a power of two. Default to 8192.
3716
    int                         TexMaxHeight;       // Maximum desired texture height. Must be a power of two. Default to 8192.
3717
    void*                       UserData;           // Store your own atlas related user-data (if e.g. you have multiple font atlas).
3718
3719
    // Output
3720
    // - Because textures are dynamically created/resized, the current texture identifier may changed at *ANY TIME* during the frame.
3721
    // - This should not affect you as you can always use the latest value. But note that any precomputed UV coordinates are only valid for the current TexRef.
3722
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3723
    ImTextureRef                TexRef;             // Latest texture identifier == TexData->GetTexRef().
3724
#else
3725
    union { ImTextureRef TexRef; ImTextureRef TexID; }; // Latest texture identifier == TexData->GetTexRef(). // RENAMED TexID to TexRef in 1.92.x
3726
#endif
3727
    ImTextureData*              TexData;            // Latest texture.
3728
3729
    // [Internal]
3730
    ImVector<ImTextureData*>    TexList;            // Texture list (most often TexList.Size == 1). TexData is always == TexList.back(). DO NOT USE DIRECTLY, USE GetDrawData().Textures[]/GetPlatformIO().Textures[] instead!
3731
    bool                        Locked;             // Marked as locked during ImGui::NewFrame()..EndFrame() scope if TexUpdates are not supported. Any attempt to modify the atlas will assert.
3732
    bool                        RendererHasTextures;// Copy of (BackendFlags & ImGuiBackendFlags_RendererHasTextures) from supporting context.
3733
    bool                        TexIsBuilt;         // Set when texture was built matching current font input. Mostly useful for legacy IsBuilt() call.
3734
    bool                        TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format or conversion process.
3735
    ImVec2                      TexUvScale;         // = (1.0f/TexData->TexWidth, 1.0f/TexData->TexHeight). May change as new texture gets created.
3736
    ImVec2                      TexUvWhitePixel;    // Texture coordinates to a white pixel. May change as new texture gets created.
3737
    ImVector<ImFont*>           Fonts;              // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.
3738
    ImVector<ImFontConfig>      Sources;            // Source/configuration data
3739
    ImVec4                      TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1];  // UVs for baked anti-aliased lines
3740
    int                         TexNextUniqueID;    // Next value to be stored in TexData->UniqueID
3741
    int                         FontNextUniqueID;   // Next value to be stored in ImFont->FontID
3742
    ImVector<ImDrawListSharedData*> DrawListSharedDatas; // List of users for this atlas. Typically one per Dear ImGui context.
3743
    ImFontAtlasBuilder*         Builder;            // Opaque interface to our data that doesn't need to be public and may be discarded when rebuilding.
3744
    const ImFontLoader*         FontLoader;         // Font loader opaque interface (default to use FreeType when IMGUI_ENABLE_FREETYPE is defined, otherwise default to use stb_truetype). Use SetFontLoader() to change this at runtime.
3745
    const char*                 FontLoaderName;     // Font loader name (for display e.g. in About box) == FontLoader->Name
3746
    void*                       FontLoaderData;     // Font backend opaque storage
3747
    unsigned int                FontLoaderFlags;    // Shared flags (for all fonts) for font loader. THIS IS BUILD IMPLEMENTATION DEPENDENT (e.g. Per-font override is also available in ImFontConfig).
3748
    int                         RefCount;           // Number of contexts using this atlas
3749
    ImGuiContext*               OwnerContext;       // Context which own the atlas will be in charge of updating and destroying it.
3750
3751
    // [Obsolete]
3752
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3753
    // Legacy: You can request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. --> Prefer using a custom ImFontLoader.
3754
    ImFontAtlasRect             TempRect;           // For old GetCustomRectByIndex() API
3755
0
    inline ImFontAtlasRectId    AddCustomRectRegular(int w, int h)                                                          { return AddCustomRect(w, h); }                             // RENAMED in 1.92.X
3756
0
    inline const ImFontAtlasRect* GetCustomRectByIndex(ImFontAtlasRectId id)                                                { return GetCustomRect(id, &TempRect) ? &TempRect : NULL; } // OBSOLETED in 1.92.X
3757
0
    inline void                 CalcCustomRectUV(const ImFontAtlasRect* r, ImVec2* out_uv_min, ImVec2* out_uv_max) const    { *out_uv_min = r->uv0; *out_uv_max = r->uv1; }             // OBSOLETED in 1.92.X
3758
    IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0));                            // OBSOLETED in 1.92.X: Use custom ImFontLoader in ImFontConfig
3759
    IMGUI_API ImFontAtlasRectId AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int w, int h, float advance_x, const ImVec2& offset = ImVec2(0, 0));    // ADDED AND OBSOLETED in 1.92.X
3760
#endif
3761
    //unsigned int                      FontBuilderFlags;        // OBSOLETED in 1.92.X: Renamed to FontLoaderFlags.
3762
    //int                               TexDesiredWidth;         // OBSOLETED in 1.92.X: Force texture width before calling Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
3763
    //typedef ImFontAtlasRect           ImFontAtlasCustomRect;   // OBSOLETED in 1.92.X
3764
    //typedef ImFontAtlasCustomRect     CustomRect;              // OBSOLETED in 1.72+
3765
    //typedef ImFontGlyphRangesBuilder  GlyphRangesBuilder;      // OBSOLETED in 1.67+
3766
};
3767
3768
// Font runtime data for a given size
3769
// Important: pointers to ImFontBaked are only valid for the current frame.
3770
struct ImFontBaked
3771
{
3772
    // [Internal] Members: Hot ~20/24 bytes (for CalcTextSize)
3773
    ImVector<float>             IndexAdvanceX;      // 12-16 // out // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI).
3774
    float                       FallbackAdvanceX;   // 4     // out // FindGlyph(FallbackChar)->AdvanceX
3775
    float                       Size;               // 4     // in  // Height of characters/line, set during loading (doesn't change after loading)
3776
    float                       RasterizerDensity;  // 4     // in  // Density this is baked at
3777
3778
    // [Internal] Members: Hot ~28/36 bytes (for RenderText loop)
3779
    ImVector<ImU16>             IndexLookup;        // 12-16 // out // Sparse. Index glyphs by Unicode code-point.
3780
    ImVector<ImFontGlyph>       Glyphs;             // 12-16 // out // All glyphs.
3781
    int                         FallbackGlyphIndex; // 4     // out // Index of FontFallbackChar
3782
3783
    // [Internal] Members: Cold
3784
    float                       Ascent, Descent;    // 4+4   // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled)
3785
    unsigned int                MetricsTotalSurface:26;// 3  // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
3786
    unsigned int                WantDestroy:1;         // 0  //     // Queued for destroy
3787
    unsigned int                LoadNoFallback:1;      // 0  //     // Disable loading fallback in lower-level calls.
3788
    unsigned int                LoadNoRenderOnLayout:1;// 0  //     // Enable a two-steps mode where CalcTextSize() calls will load AdvanceX *without* rendering/packing glyphs. Only advantagous if you know that the glyph is unlikely to actually be rendered, otherwise it is slower because we'd do one query on the first CalcTextSize and one query on the first Draw.
3789
    int                         LastUsedFrame;         // 4  //     // Record of that time this was bounds
3790
    ImGuiID                     BakedId;            // 4     //     // Unique ID for this baked storage
3791
    ImFont*                     ContainerFont;      // 4-8   // in  // Parent font
3792
    void*                       FontLoaderDatas;    // 4-8   //     // Font loader opaque storage (per baked font * sources): single contiguous buffer allocated by imgui, passed to loader.
3793
3794
    // Functions
3795
    IMGUI_API ImFontBaked();
3796
    IMGUI_API void              ClearOutputData();
3797
    IMGUI_API ImFontGlyph*      FindGlyph(ImWchar c);               // Return U+FFFD glyph if requested glyph doesn't exists.
3798
    IMGUI_API ImFontGlyph*      FindGlyphNoFallback(ImWchar c);     // Return NULL if glyph doesn't exist
3799
    IMGUI_API float             GetCharAdvance(ImWchar c);
3800
    IMGUI_API bool              IsGlyphLoaded(ImWchar c);
3801
};
3802
3803
// Font flags
3804
// (in future versions as we redesign font loading API, this will become more important and better documented. for now please consider this as internal/advanced use)
3805
enum ImFontFlags_
3806
{
3807
    ImFontFlags_None                    = 0,
3808
    ImFontFlags_NoLoadError             = 1 << 1,   // Disable throwing an error/assert when calling AddFontXXX() with missing file/data. Calling code is expected to check AddFontXXX() return value.
3809
    ImFontFlags_NoLoadGlyphs            = 1 << 2,   // [Internal] Disable loading new glyphs.
3810
    ImFontFlags_LockBakedSizes          = 1 << 3,   // [Internal] Disable loading new baked sizes, disable garbage collecting current ones. e.g. if you want to lock a font to a single size. Important: if you use this to preload given sizes, consider the possibility of multiple font density used on Retina display.
3811
};
3812
3813
// Font runtime data and rendering
3814
// - ImFontAtlas automatically loads a default embedded font for you if you didn't load one manually.
3815
// - Since 1.92.X a font may be rendered as any size! Therefore a font doesn't have one specific size.
3816
// - Use 'font->GetFontBaked(size)' to retrieve the ImFontBaked* corresponding to a given size.
3817
// - If you used g.Font + g.FontSize (which is frequent from the ImGui layer), you can use g.FontBaked as a shortcut, as g.FontBaked == g.Font->GetFontBaked(g.FontSize).
3818
struct ImFont
3819
{
3820
    // [Internal] Members: Hot ~12-20 bytes
3821
    ImFontBaked*                LastBaked;          // 4-8   // Cache last bound baked. NEVER USE DIRECTLY. Use GetFontBaked().
3822
    ImFontAtlas*                ContainerAtlas;     // 4-8   // What we have been loaded into.
3823
    ImFontFlags                 Flags;              // 4     // Font flags.
3824
    float                       CurrentRasterizerDensity;    // Current rasterizer density. This is a varying state of the font.
3825
3826
    // [Internal] Members: Cold ~24-52 bytes
3827
    // Conceptually Sources[] is the list of font sources merged to create this font.
3828
    ImGuiID                     FontId;             // Unique identifier for the font
3829
    float                       LegacySize;         // 4     // in  // Font size passed to AddFont(). Use for old code calling PushFont() expecting to use that size. (use ImGui::GetFontBaked() to get font baked at current bound size).
3830
    ImVector<ImFontConfig*>     Sources;            // 16    // in  // List of sources. Pointers within ContainerAtlas->Sources[]
3831
    ImWchar                     EllipsisChar;       // 2-4   // out // Character used for ellipsis rendering ('...').
3832
    ImWchar                     FallbackChar;       // 2-4   // out // Character used if a glyph isn't found (U+FFFD, '?')
3833
    ImU8                        Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.
3834
    bool                        EllipsisAutoBake;   // 1     //     // Mark when the "..." glyph needs to be generated.
3835
    ImGuiStorage                RemapPairs;         // 16    //     // Remapping pairs when using AddRemapChar(), otherwise empty.
3836
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3837
    float                       Scale;              // 4     // in  // Legacy base font scale (~1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
3838
#endif
3839
3840
    // Methods
3841
    IMGUI_API ImFont();
3842
    IMGUI_API ~ImFont();
3843
    IMGUI_API bool              IsGlyphInFont(ImWchar c);
3844
241k
    bool                        IsLoaded() const                { return ContainerAtlas != NULL; }
3845
0
    const char*                 GetDebugName() const            { return Sources.Size ? Sources[0]->Name : "<unknown>"; } // Fill ImFontConfig::Name.
3846
3847
    // [Internal] Don't use!
3848
    // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.
3849
    // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.
3850
    IMGUI_API ImFontBaked*      GetFontBaked(float font_size, float density = -1.0f);  // Get or create baked data for given size
3851
    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** out_remaining = NULL);
3852
    IMGUI_API const char*       CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width);
3853
    IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip = NULL);
3854
    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, ImDrawTextFlags flags = 0);
3855
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3856
0
    inline const char*          CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) { return CalcWordWrapPosition(LegacySize * scale, text, text_end, wrap_width); }
3857
#endif
3858
3859
    // [Internal] Don't use!
3860
    IMGUI_API void              ClearOutputData();
3861
    IMGUI_API void              AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint); // Makes 'from_codepoint' character points to 'to_codepoint' glyph.
3862
    IMGUI_API bool              IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last);
3863
};
3864
3865
// This is provided for consistency (but we don't actually use this)
3866
inline ImTextureID ImTextureRef::GetTexID() const
3867
0
{
3868
0
    IM_ASSERT(!(_TexData != NULL && _TexID != ImTextureID_Invalid));
3869
0
    return _TexData ? _TexData->TexID : _TexID;
3870
0
}
3871
3872
// Using an indirection to avoid patching ImDrawCmd after a SetTexID() call (but this could be an alternative solution too)
3873
inline ImTextureID ImDrawCmd::GetTexID() const
3874
162k
{
3875
    // If you are getting this assert: A renderer backend with support for ImGuiBackendFlags_RendererHasTextures (1.92)
3876
    // must iterate and handle ImTextureData requests stored in ImDrawData::Textures[].
3877
162k
    ImTextureID tex_id = TexRef._TexData ? TexRef._TexData->TexID : TexRef._TexID; // == TexRef.GetTexID() above.
3878
162k
    if (TexRef._TexData != NULL)
3879
162k
        IM_ASSERT(tex_id != ImTextureID_Invalid && "ImDrawCmd is referring to ImTextureData that wasn't uploaded to graphics system. Backend must call ImTextureData::SetTexID() after handling ImTextureStatus_WantCreate request!");
3880
162k
    return tex_id;
3881
162k
}
3882
3883
//-----------------------------------------------------------------------------
3884
// [SECTION] Viewports
3885
//-----------------------------------------------------------------------------
3886
3887
// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends.
3888
enum ImGuiViewportFlags_
3889
{
3890
    ImGuiViewportFlags_None                     = 0,
3891
    ImGuiViewportFlags_IsPlatformWindow         = 1 << 0,   // Represent a Platform Window
3892
    ImGuiViewportFlags_IsPlatformMonitor        = 1 << 1,   // Represent a Platform Monitor (unused yet)
3893
    ImGuiViewportFlags_OwnedByApp               = 1 << 2,   // Platform Window: Is created/managed by the application (rather than a dear imgui backend)
3894
};
3895
3896
// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.
3897
// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.
3898
// - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode.
3899
// - About Main Area vs Work Area:
3900
//   - Main Area = entire viewport.
3901
//   - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor).
3902
//   - Windows are generally trying to stay within the Work Area of their host viewport.
3903
struct ImGuiViewport
3904
{
3905
    ImGuiID             ID;                     // Unique identifier for the viewport
3906
    ImGuiViewportFlags  Flags;                  // See ImGuiViewportFlags_
3907
    ImVec2              Pos;                    // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates)
3908
    ImVec2              Size;                   // Main Area: Size of the viewport.
3909
    ImVec2              FramebufferScale;       // Density of the viewport for Retina display (always 1,1 on Windows, may be 2,2 etc on macOS/iOS). This will affect font rasterizer density.
3910
    ImVec2              WorkPos;                // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos)
3911
    ImVec2              WorkSize;               // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)
3912
3913
    // Platform/Backend Dependent Data
3914
    void*               PlatformHandle;         // void* to hold higher-level, platform window handle (e.g. HWND, GLFWWindow*, SDL_Window*)
3915
    void*               PlatformHandleRaw;      // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms)
3916
3917
1
    ImGuiViewport()     { memset(this, 0, sizeof(*this)); }
3918
3919
    // Helpers
3920
0
    ImVec2              GetCenter() const       { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); }
3921
0
    ImVec2              GetWorkCenter() const   { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); }
3922
};
3923
3924
//-----------------------------------------------------------------------------
3925
// [SECTION] Platform Dependent Interfaces
3926
//-----------------------------------------------------------------------------
3927
3928
// Access via ImGui::GetPlatformIO()
3929
struct ImGuiPlatformIO
3930
{
3931
    IMGUI_API ImGuiPlatformIO();
3932
3933
    //------------------------------------------------------------------
3934
    // Input - Interface with OS and Platform backend (most common stuff)
3935
    //------------------------------------------------------------------
3936
3937
    // Optional: Access OS clipboard
3938
    // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures)
3939
    const char* (*Platform_GetClipboardTextFn)(ImGuiContext* ctx);
3940
    void        (*Platform_SetClipboardTextFn)(ImGuiContext* ctx, const char* text);
3941
    void*       Platform_ClipboardUserData;
3942
3943
    // Optional: Open link/folder/file in OS Shell
3944
    // (default to use ShellExecuteW() on Windows, system() on Linux/Mac)
3945
    bool        (*Platform_OpenInShellFn)(ImGuiContext* ctx, const char* path);
3946
    void*       Platform_OpenInShellUserData;
3947
3948
    // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)
3949
    // (default to use native imm32 api on Windows)
3950
    void        (*Platform_SetImeDataFn)(ImGuiContext* ctx, ImGuiViewport* viewport, ImGuiPlatformImeData* data);
3951
    void*       Platform_ImeUserData;
3952
    //void      (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); // [Renamed to platform_io.PlatformSetImeDataFn in 1.91.1]
3953
3954
    // Optional: Platform locale
3955
    // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point
3956
    ImWchar     Platform_LocaleDecimalPoint;     // '.'
3957
3958
    //------------------------------------------------------------------
3959
    // Input - Interface with Renderer Backend
3960
    //------------------------------------------------------------------
3961
3962
    // Optional: Maximum texture size supported by renderer (used to adjust how we size textures). 0 if not known.
3963
    int         Renderer_TextureMaxWidth;
3964
    int         Renderer_TextureMaxHeight;
3965
3966
    // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure.
3967
    void*       Renderer_RenderState;
3968
3969
    //------------------------------------------------------------------
3970
    // Output
3971
    //------------------------------------------------------------------
3972
3973
    // Textures list (the list is updated by calling ImGui::EndFrame or ImGui::Render)
3974
    // The ImGui_ImplXXXX_RenderDrawData() function of each backend generally access this via ImDrawData::Textures which points to this. The array is available here mostly because backends will want to destroy textures on shutdown.
3975
    ImVector<ImTextureData*>        Textures;           // List of textures used by Dear ImGui (most often 1) + contents of external texture list is automatically appended into this.
3976
3977
    //------------------------------------------------------------------
3978
    // Functions
3979
    //------------------------------------------------------------------
3980
3981
    void    ClearPlatformHandlers();    // Clear all Platform_XXX fields. Typically called on Platform Backend shutdown.
3982
    void    ClearRendererHandlers();    // Clear all Renderer_XXX fields. Typically called on Renderer Backend shutdown.
3983
};
3984
3985
// (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. Handler is called during EndFrame().
3986
struct ImGuiPlatformImeData
3987
{
3988
    bool    WantVisible;            // A widget wants the IME to be visible.
3989
    bool    WantTextInput;          // A widget wants text input, not necessarily IME to be visible. This is automatically set to the upcoming value of io.WantTextInput.
3990
    ImVec2  InputPos;               // Position of input cursor (for IME).
3991
    float   InputLineHeight;        // Line height (for IME).
3992
    ImGuiID ViewportId;             // ID of platform window/viewport.
3993
3994
2
    ImGuiPlatformImeData()          { memset(this, 0, sizeof(*this)); }
3995
};
3996
3997
//-----------------------------------------------------------------------------
3998
// [SECTION] Obsolete functions and types
3999
// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details)
4000
// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.
4001
//-----------------------------------------------------------------------------
4002
4003
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4004
namespace ImGui
4005
{
4006
    // OBSOLETED in 1.92.0 (from June 2025)
4007
0
    inline void         PushFont(ImFont* font)                                  { PushFont(font, font ? font->LegacySize : 0.0f); }
4008
    IMGUI_API void      SetWindowFontScale(float scale);                        // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows.
4009
    // OBSOLETED in 1.91.9 (from February 2025)
4010
    IMGUI_API void      Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col); // <-- 'border_col' was removed in favor of ImGuiCol_ImageBorder. If you use 'tint_col', use ImageWithBg() instead.
4011
    // OBSOLETED in 1.91.0 (from July 2024)
4012
0
    inline void         PushButtonRepeat(bool repeat)                           { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); }
4013
0
    inline void         PopButtonRepeat()                                       { PopItemFlag(); }
4014
0
    inline void         PushTabStop(bool tab_stop)                              { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
4015
0
    inline void         PopTabStop()                                            { PopItemFlag(); }
4016
    IMGUI_API ImVec2    GetContentRegionMax();                                  // Content boundaries max (e.g. window boundaries including scrolling, or current column boundaries). You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!
4017
    IMGUI_API ImVec2    GetWindowContentRegionMin();                            // Content boundaries min for the window (roughly (0,0)-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!
4018
    IMGUI_API ImVec2    GetWindowContentRegionMax();                            // Content boundaries max for the window (roughly (0,0)+Size-Scroll), in window-local coordinates. You should never need this. Always use GetCursorScreenPos() and GetContentRegionAvail()!
4019
    // OBSOLETED in 1.90.0 (from September 2023)
4020
0
    inline bool         BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0)  { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); }
4021
0
    inline void         EndChildFrame()                                                                     { EndChild(); }
4022
    //inline bool       BeginChild(const char* str_id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Borders
4023
    //inline bool       BeginChild(ImGuiID id, const ImVec2& size_arg, bool borders, ImGuiWindowFlags window_flags)        { return BeginChild(id, size_arg, borders ? ImGuiChildFlags_Borders : ImGuiChildFlags_None, window_flags);     } // Unnecessary as true == ImGuiChildFlags_Borders
4024
0
    inline void         ShowStackToolWindow(bool* p_open = NULL)                { ShowIDStackToolWindow(p_open); }
4025
    IMGUI_API bool      Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1);
4026
    IMGUI_API bool      ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1);
4027
    // OBSOLETED in 1.89.7 (from June 2023)
4028
    IMGUI_API void      SetItemAllowOverlap();                                  // Use SetNextItemAllowOverlap() before item.
4029
4030
    // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)
4031
    //-- OBSOLETED in 1.89.4 (from March 2023)
4032
    //static inline void  PushAllowKeyboardFocus(bool tab_stop)                                       { PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop); }
4033
    //static inline void  PopAllowKeyboardFocus()                                                     { PopItemFlag(); }
4034
    //-- OBSOLETED in 1.89 (from August 2022)
4035
    //IMGUI_API bool      ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // --> Use new ImageButton() signature (explicit item id, regular FramePadding). Refer to code in 1.91 if you want to grab a copy of this version.
4036
    //-- OBSOLETED in 1.88 (from May 2022)
4037
    //static inline void  CaptureKeyboardFromApp(bool want_capture_keyboard = true)                   { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.
4038
    //static inline void  CaptureMouseFromApp(bool want_capture_mouse = true)                         { SetNextFrameWantCaptureMouse(want_capture_mouse); }       // Renamed as name was misleading + removed default value.
4039
    //-- OBSOLETED in 1.87 (from February 2022, more formally obsoleted April 2024)
4040
    //IMGUI_API ImGuiKey  GetKeyIndex(ImGuiKey key);                                                  { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); const ImGuiKeyData* key_data = GetKeyData(key); return (ImGuiKey)(key_data - g.IO.KeysData); } // Map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]. When using a 1.87+ backend using io.AddKeyEvent(), calling GetKeyIndex() with ANY ImGuiKey_XXXX values will return the same value!
4041
    //static inline ImGuiKey GetKeyIndex(ImGuiKey key)                                                { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END); return key; }
4042
    //-- OBSOLETED in 1.86 (from November 2021)
4043
    //IMGUI_API void      CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper.
4044
    //-- OBSOLETED in 1.85 (from August 2021)
4045
    //static inline float GetWindowContentRegionWidth()                                               { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
4046
    //-- OBSOLETED in 1.81 (from February 2021)
4047
    //static inline bool  ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0))         { return BeginListBox(label, size); }
4048
    //static inline bool  ListBoxHeader(const char* label, int items_count, int height_in_items = -1) { float height = GetTextLineHeightWithSpacing() * ((height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f) + GetStyle().FramePadding.y * 2.0f; return BeginListBox(label, ImVec2(0.0f, height)); } // Helper to calculate size from items_count and height_in_items
4049
    //static inline void  ListBoxFooter()                                                             { EndListBox(); }
4050
    //-- OBSOLETED in 1.79 (from August 2020)
4051
    //static inline void  OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1)    { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry!
4052
    //-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details.
4053
    //IMGUI_API bool      DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f)                                                            // OBSOLETED in 1.78 (from June 2020)
4054
    //IMGUI_API bool      DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                          // OBSOLETED in 1.78 (from June 2020)
4055
    //IMGUI_API bool      SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                                                        // OBSOLETED in 1.78 (from June 2020)
4056
    //IMGUI_API bool      SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                                       // OBSOLETED in 1.78 (from June 2020)
4057
    //static inline bool  DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power = 1.0f)    { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); }     // OBSOLETED in 1.78 (from June 2020)
4058
    //static inline bool  DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)
4059
    //static inline bool  DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)
4060
    //static inline bool  DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)
4061
    //static inline bool  SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power = 1.0f)                 { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); }            // OBSOLETED in 1.78 (from June 2020)
4062
    //static inline bool  SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)
4063
    //static inline bool  SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)
4064
    //static inline bool  SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)
4065
    //-- OBSOLETED in 1.77 and before
4066
    //static inline bool  BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } // OBSOLETED in 1.77 (from June 2020)
4067
    //static inline void  TreeAdvanceToLabelPos()               { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }   // OBSOLETED in 1.72 (from July 2019)
4068
    //static inline void  SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); }                       // OBSOLETED in 1.71 (from June 2019)
4069
    //static inline float GetContentRegionAvailWidth()          { return GetContentRegionAvail().x; }                               // OBSOLETED in 1.70 (from May 2019)
4070
    //static inline ImDrawList* GetOverlayDrawList()            { return GetForegroundDrawList(); }                                 // OBSOLETED in 1.69 (from Mar 2019)
4071
    //static inline void  SetScrollHere(float ratio = 0.5f)     { SetScrollHereY(ratio); }                                          // OBSOLETED in 1.66 (from Nov 2018)
4072
    //static inline bool  IsItemDeactivatedAfterChange()        { return IsItemDeactivatedAfterEdit(); }                            // OBSOLETED in 1.63 (from Aug 2018)
4073
    //-- OBSOLETED in 1.60 and before
4074
    //static inline bool  IsAnyWindowFocused()                  { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }            // OBSOLETED in 1.60 (from Apr 2018)
4075
    //static inline bool  IsAnyWindowHovered()                  { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)
4076
    //static inline void  ShowTestWindow()                      { return ShowDemoWindow(); }                                        // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
4077
    //static inline bool  IsRootWindowFocused()                 { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); }           // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
4078
    //static inline bool  IsRootWindowOrAnyChildFocused()       { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); }  // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
4079
    //static inline void  SetNextWindowContentWidth(float w)    { SetNextWindowContentSize(ImVec2(w, 0.0f)); }                      // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
4080
    //static inline float GetItemsLineHeightWithSpacing()       { return GetFrameHeightWithSpacing(); }                             // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)
4081
    //IMGUI_API bool      Begin(char* name, bool* p_open, ImVec2 size_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags=0); // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017): Equivalent of using SetNextWindowSize(size, ImGuiCond_FirstUseEver) and SetNextWindowBgAlpha().
4082
    //static inline bool  IsRootWindowOrAnyChildHovered()       { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); }  // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)
4083
    //static inline void  AlignFirstTextHeightToWidgets()       { AlignTextToFramePadding(); }                                      // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)
4084
    //static inline void  SetNextWindowPosCenter(ImGuiCond c=0) { SetNextWindowPos(GetMainViewport()->GetCenter(), c, ImVec2(0.5f,0.5f)); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)
4085
    //static inline bool  IsItemHoveredRect()                   { return IsItemHovered(ImGuiHoveredFlags_RectOnly); }               // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)
4086
    //static inline bool  IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; }                                     // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017): This was misleading and partly broken. You probably want to use the io.WantCaptureMouse flag instead.
4087
    //static inline bool  IsMouseHoveringAnyWindow()            { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)
4088
    //static inline bool  IsMouseHoveringWindow()               { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); }       // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)
4089
    //-- OBSOLETED in 1.50 and before
4090
    //static inline bool  CollapsingHeader(char* label, const char* str_id, bool framed = true, bool default_open = false) { return CollapsingHeader(label, (default_open ? (1 << 5) : 0)); } // OBSOLETED in 1.49
4091
    //static inline ImFont*GetWindowFont()                      { return GetFont(); }                                               // OBSOLETED in 1.48
4092
    //static inline float GetWindowFontSize()                   { return GetFontSize(); }                                           // OBSOLETED in 1.48
4093
    //static inline void  SetScrollPosHere()                    { SetScrollHere(); }                                                // OBSOLETED in 1.42
4094
}
4095
4096
//-- OBSOLETED in 1.92.x: ImFontAtlasCustomRect becomes ImTextureRect
4097
// - ImFontAtlasCustomRect::X,Y          --> ImTextureRect::x,y
4098
// - ImFontAtlasCustomRect::Width,Height --> ImTextureRect::w,h
4099
// - ImFontAtlasCustomRect::GlyphColored --> if you need to write to this, instead you can write to 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph()
4100
// We could make ImTextureRect an union to use old names, but 1) this would be confusing 2) the fix is easy 3) ImFontAtlasCustomRect was always a rather esoteric api.
4101
typedef ImFontAtlasRect ImFontAtlasCustomRect;
4102
/*struct ImFontAtlasCustomRect
4103
{
4104
    unsigned short  X, Y;           // Output   // Packed position in Atlas
4105
    unsigned short  Width, Height;  // Input    // [Internal] Desired rectangle dimension
4106
    unsigned int    GlyphID:31;     // Input    // [Internal] For custom font glyphs only (ID < 0x110000)
4107
    unsigned int    GlyphColored:1; // Input    // [Internal] For custom font glyphs only: glyph is colored, removed tinting.
4108
    float           GlyphAdvanceX;  // Input    // [Internal] For custom font glyphs only: glyph xadvance
4109
    ImVec2          GlyphOffset;    // Input    // [Internal] For custom font glyphs only: glyph display offset
4110
    ImFont*         Font;           // Input    // [Internal] For custom font glyphs only: target font
4111
    ImFontAtlasCustomRect()         { X = Y = 0xFFFF; Width = Height = 0; GlyphID = 0; GlyphColored = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }
4112
    bool IsPacked() const           { return X != 0xFFFF; }
4113
};*/
4114
4115
//-- OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()
4116
//typedef ImDrawFlags ImDrawCornerFlags;
4117
//enum ImDrawCornerFlags_
4118
//{
4119
//    ImDrawCornerFlags_None      = ImDrawFlags_RoundCornersNone,         // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit
4120
//    ImDrawCornerFlags_TopLeft   = ImDrawFlags_RoundCornersTopLeft,      // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally).
4121
//    ImDrawCornerFlags_TopRight  = ImDrawFlags_RoundCornersTopRight,     // Was == 0x02 (1 << 1) prior to 1.82.
4122
//    ImDrawCornerFlags_BotLeft   = ImDrawFlags_RoundCornersBottomLeft,   // Was == 0x04 (1 << 2) prior to 1.82.
4123
//    ImDrawCornerFlags_BotRight  = ImDrawFlags_RoundCornersBottomRight,  // Was == 0x08 (1 << 3) prior to 1.82.
4124
//    ImDrawCornerFlags_All       = ImDrawFlags_RoundCornersAll,          // Was == 0x0F prior to 1.82
4125
//    ImDrawCornerFlags_Top       = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight,
4126
//    ImDrawCornerFlags_Bot       = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight,
4127
//    ImDrawCornerFlags_Left      = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft,
4128
//    ImDrawCornerFlags_Right     = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight,
4129
//};
4130
4131
// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022)
4132
// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place.
4133
//typedef ImGuiKeyChord ImGuiModFlags;      // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", so you may store mods in there.
4134
//enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super };
4135
//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int
4136
//enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super };
4137
4138
#define IM_OFFSETOF(_TYPE,_MEMBER)  offsetof(_TYPE, _MEMBER)    // OBSOLETED IN 1.90 (now using C++11 standard version)
4139
4140
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4141
4142
// RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022)
4143
#ifdef IMGUI_DISABLE_METRICS_WINDOW
4144
#error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name.
4145
#endif
4146
4147
//-----------------------------------------------------------------------------
4148
4149
#if defined(__clang__)
4150
#pragma clang diagnostic pop
4151
#elif defined(__GNUC__)
4152
#pragma GCC diagnostic pop
4153
#endif
4154
4155
#ifdef _MSC_VER
4156
#pragma warning (pop)
4157
#endif
4158
4159
// Include imgui_user.h at the end of imgui.h
4160
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
4161
#ifdef IMGUI_INCLUDE_IMGUI_USER_H
4162
#ifdef IMGUI_USER_H_FILENAME
4163
#include IMGUI_USER_H_FILENAME
4164
#else
4165
#include "imgui_user.h"
4166
#endif
4167
#endif
4168
4169
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui_demo.cpp
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (demo code)
3
4
// Help:
5
// - Read FAQ at http://dearimgui.com/faq
6
// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.
7
// - Need help integrating Dear ImGui in your codebase?
8
//   - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started
9
//   - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.
10
// Read top of imgui.cpp and imgui.h for many details, documentation, comments, links.
11
// Get the latest version at https://github.com/ocornut/imgui
12
13
// How to easily locate code?
14
// - Use Tools->Item Picker to debug break in code by clicking any widgets: https://github.com/ocornut/imgui/wiki/Debug-Tools
15
// - Browse an online version the demo with code linked to hovered widgets: https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html
16
// - Find a visible string and search for it in the code!
17
18
//---------------------------------------------------
19
// PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!
20
//---------------------------------------------------
21
// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:
22
// Think again! It is the most useful reference code that you and other coders will want to refer to and call.
23
// Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app!
24
// Also include Metrics! ItemPicker! DebugLog! and other debug features.
25
// Removing this file from your project is hindering access to documentation for everyone in your team,
26
// likely leading you to poorer usage of the library.
27
// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().
28
// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be
29
// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.
30
// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.
31
// Thank you,
32
// -Your beloved friend, imgui_demo.cpp (which you won't delete)
33
34
//--------------------------------------------
35
// ABOUT THE MEANING OF THE 'static' KEYWORD:
36
//--------------------------------------------
37
// In this demo code, we frequently use 'static' variables inside functions.
38
// A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function.
39
// Think of "static int n = 0;" as "global int n = 0;" !
40
// We do this IN THE DEMO because we want:
41
// - to gather code and data in the same place.
42
// - to make the demo source code faster to read, faster to change, smaller in size.
43
// - it is also a convenient way of storing simple UI related information as long as your function
44
//   doesn't need to be reentrant or used in multiple threads.
45
// This might be a pattern you will want to use in your code, but most of the data you would be working
46
// with in a complex codebase is likely going to be stored outside your functions.
47
48
//-----------------------------------------
49
// ABOUT THE CODING STYLE OF OUR DEMO CODE
50
//-----------------------------------------
51
// The Demo code in this file is designed to be easy to copy-and-paste into your application!
52
// Because of this:
53
// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.
54
// - We try to declare static variables in the local scope, as close as possible to the code using them.
55
// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.
56
// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided
57
//   by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional
58
//   and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.
59
//   Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.
60
61
// Navigating this file:
62
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
63
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
64
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
65
// - You can search/grep for all sections listed in the index to find the section.
66
67
/*
68
69
Index of this file:
70
71
// [SECTION] Forward Declarations
72
// [SECTION] Helpers
73
// [SECTION] Demo Window / ShowDemoWindow()
74
// [SECTION] DemoWindowMenuBar()
75
// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
76
// [SECTION] DemoWindowWidgetsBasic()
77
// [SECTION] DemoWindowWidgetsBullets()
78
// [SECTION] DemoWindowWidgetsCollapsingHeaders()
79
// [SECTION] DemoWindowWidgetsComboBoxes()
80
// [SECTION] DemoWindowWidgetsColorAndPickers()
81
// [SECTION] DemoWindowWidgetsDataTypes()
82
// [SECTION] DemoWindowWidgetsDisableBlocks()
83
// [SECTION] DemoWindowWidgetsDragAndDrop()
84
// [SECTION] DemoWindowWidgetsDragsAndSliders()
85
// [SECTION] DemoWindowWidgetsFonts()
86
// [SECTION] DemoWindowWidgetsImages()
87
// [SECTION] DemoWindowWidgetsListBoxes()
88
// [SECTION] DemoWindowWidgetsMultiComponents()
89
// [SECTION] DemoWindowWidgetsPlotting()
90
// [SECTION] DemoWindowWidgetsProgressBars()
91
// [SECTION] DemoWindowWidgetsQueryingStatuses()
92
// [SECTION] DemoWindowWidgetsSelectables()
93
// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
94
// [SECTION] DemoWindowWidgetsTabs()
95
// [SECTION] DemoWindowWidgetsText()
96
// [SECTION] DemoWindowWidgetsTextFilter()
97
// [SECTION] DemoWindowWidgetsTextInput()
98
// [SECTION] DemoWindowWidgetsTooltips()
99
// [SECTION] DemoWindowWidgetsTreeNodes()
100
// [SECTION] DemoWindowWidgetsVerticalSliders()
101
// [SECTION] DemoWindowWidgets()
102
// [SECTION] DemoWindowLayout()
103
// [SECTION] DemoWindowPopups()
104
// [SECTION] DemoWindowTables()
105
// [SECTION] DemoWindowInputs()
106
// [SECTION] About Window / ShowAboutWindow()
107
// [SECTION] Style Editor / ShowStyleEditor()
108
// [SECTION] User Guide / ShowUserGuide()
109
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
110
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
111
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
112
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
113
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
114
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
115
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
116
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
117
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
118
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
119
// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()
120
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
121
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
122
// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
123
124
*/
125
126
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
127
#define _CRT_SECURE_NO_WARNINGS
128
#endif
129
130
#include "imgui.h"
131
#ifndef IMGUI_DISABLE
132
133
// System includes
134
#include <ctype.h>          // toupper
135
#include <limits.h>         // INT_MIN, INT_MAX
136
#include <math.h>           // sqrtf, powf, cosf, sinf, floorf, ceilf
137
#include <stdio.h>          // vsnprintf, sscanf, printf
138
#include <stdlib.h>         // NULL, malloc, free, atoi
139
#include <stdint.h>         // intptr_t
140
#if !defined(_MSC_VER) || _MSC_VER >= 1800
141
#include <inttypes.h>       // PRId64/PRIu64, not avail in some MinGW headers.
142
#endif
143
#ifdef __EMSCRIPTEN__
144
#include <emscripten/version.h>     // __EMSCRIPTEN_major__ etc.
145
#endif
146
147
// Visual Studio warnings
148
#ifdef _MSC_VER
149
#pragma warning (disable: 4127)     // condition expression is constant
150
#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
151
#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
152
#endif
153
154
// Clang/GCC warnings with -Weverything
155
#if defined(__clang__)
156
#if __has_warning("-Wunknown-warning-option")
157
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                     // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
158
#endif
159
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
160
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                           // yes, they are more terse.
161
#pragma clang diagnostic ignored "-Wdeprecated-declarations"        // warning: 'xx' is deprecated: The POSIX name for this..   // for strdup used in demo code (so user can copy & paste the code)
162
#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast"       // warning: cast to 'void *' from smaller integer type
163
#pragma clang diagnostic ignored "-Wformat"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'
164
#pragma clang diagnostic ignored "-Wformat-security"                // warning: format string is not a string literal
165
#pragma clang diagnostic ignored "-Wexit-time-destructors"          // warning: declaration requires an exit-time destructor    // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.
166
#pragma clang diagnostic ignored "-Wunused-macros"                  // warning: macro is not used                               // we define snprintf/vsnprintf on Windows so they are available, but not always used.
167
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                   // some standard header variations use #define NULL 0
168
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
169
#pragma clang diagnostic ignored "-Wreserved-id-macro"              // warning: macro name is a reserved identifier
170
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
171
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
172
#pragma clang diagnostic ignored "-Wswitch-default"                 // warning: 'switch' missing 'default' label
173
#elif defined(__GNUC__)
174
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
175
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
176
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"              // warning: cast to pointer from integer of different size
177
#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
178
#pragma GCC diagnostic ignored "-Wformat-security"                  // warning: format string is not a string literal (potentially insecure)
179
#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
180
#pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
181
#pragma GCC diagnostic ignored "-Wmisleading-indentation"           // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.
182
#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
183
#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
184
#endif
185
186
// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)
187
#ifdef _WIN32
188
#define IM_NEWLINE  "\r\n"
189
#else
190
0
#define IM_NEWLINE  "\n"
191
#endif
192
193
// Helpers
194
#if defined(_MSC_VER) && !defined(snprintf)
195
#define snprintf    _snprintf
196
#endif
197
#if defined(_MSC_VER) && !defined(vsnprintf)
198
#define vsnprintf   _vsnprintf
199
#endif
200
201
// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)
202
#if !defined(PRId64) && defined(_MSC_VER)
203
#define PRId64 "I64d"
204
#define PRIu64 "I64u"
205
#elif !defined(PRId64)
206
#define PRId64 "lld"
207
#define PRIu64 "llu"
208
#endif
209
210
// Helpers macros
211
// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,
212
// but making an exception here as those are largely simplifying code...
213
// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.
214
0
#define IM_MIN(A, B)            (((A) < (B)) ? (A) : (B))
215
0
#define IM_MAX(A, B)            (((A) >= (B)) ? (A) : (B))
216
0
#define IM_CLAMP(V, MN, MX)     ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))
217
218
// Enforce cdecl calling convention for functions called by the standard library,
219
// in case compilation settings changed the default to e.g. __vectorcall
220
#ifndef IMGUI_CDECL
221
#ifdef _MSC_VER
222
#define IMGUI_CDECL __cdecl
223
#else
224
#define IMGUI_CDECL
225
#endif
226
#endif
227
228
//-----------------------------------------------------------------------------
229
// [SECTION] Forward Declarations
230
//-----------------------------------------------------------------------------
231
232
#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)
233
234
// Forward Declarations
235
struct ImGuiDemoWindowData;
236
static void ShowExampleAppMainMenuBar();
237
static void ShowExampleAppAssetsBrowser(bool* p_open);
238
static void ShowExampleAppConsole(bool* p_open);
239
static void ShowExampleAppCustomRendering(bool* p_open);
240
static void ShowExampleAppDocuments(bool* p_open);
241
static void ShowExampleAppLog(bool* p_open);
242
static void ShowExampleAppLayout(bool* p_open);
243
static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data);
244
static void ShowExampleAppSimpleOverlay(bool* p_open);
245
static void ShowExampleAppAutoResize(bool* p_open);
246
static void ShowExampleAppConstrainedResize(bool* p_open);
247
static void ShowExampleAppFullscreen(bool* p_open);
248
static void ShowExampleAppLongText(bool* p_open);
249
static void ShowExampleAppWindowTitles(bool* p_open);
250
static void ShowExampleMenuFile();
251
252
// We split the contents of the big ShowDemoWindow() function into smaller functions
253
// (because the link time of very large functions tends to grow non-linearly)
254
static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data);
255
static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data);
256
static void DemoWindowLayout();
257
static void DemoWindowPopups();
258
static void DemoWindowTables();
259
static void DemoWindowColumns();
260
static void DemoWindowInputs();
261
262
// Helper tree functions used by Property Editor & Multi-Select demos
263
struct ExampleTreeNode;
264
static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent);
265
static void             ExampleTree_DestroyNode(ExampleTreeNode* node);
266
267
//-----------------------------------------------------------------------------
268
// [SECTION] Helpers
269
//-----------------------------------------------------------------------------
270
271
// Helper to display a little (?) mark which shows a tooltip when hovered.
272
// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)
273
static void HelpMarker(const char* desc)
274
0
{
275
0
    ImGui::TextDisabled("(?)");
276
0
    if (ImGui::BeginItemTooltip())
277
0
    {
278
0
        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
279
0
        ImGui::TextUnformatted(desc);
280
0
        ImGui::PopTextWrapPos();
281
0
        ImGui::EndTooltip();
282
0
    }
283
0
}
284
285
// Helper to wire demo markers located in code to an interactive browser
286
typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);
287
extern ImGuiDemoMarkerCallback      GImGuiDemoMarkerCallback;
288
extern void*                        GImGuiDemoMarkerCallbackUserData;
289
ImGuiDemoMarkerCallback             GImGuiDemoMarkerCallback = NULL;
290
void*                               GImGuiDemoMarkerCallbackUserData = NULL;
291
0
#define IMGUI_DEMO_MARKER(section)  do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)
292
293
//-----------------------------------------------------------------------------
294
// [SECTION] Demo Window / ShowDemoWindow()
295
//-----------------------------------------------------------------------------
296
297
// Data to be shared across different functions of the demo.
298
struct ImGuiDemoWindowData
299
{
300
    // Examples Apps (accessible from the "Examples" menu)
301
    bool ShowMainMenuBar = false;
302
    bool ShowAppAssetsBrowser = false;
303
    bool ShowAppConsole = false;
304
    bool ShowAppCustomRendering = false;
305
    bool ShowAppDocuments = false;
306
    bool ShowAppLog = false;
307
    bool ShowAppLayout = false;
308
    bool ShowAppPropertyEditor = false;
309
    bool ShowAppSimpleOverlay = false;
310
    bool ShowAppAutoResize = false;
311
    bool ShowAppConstrainedResize = false;
312
    bool ShowAppFullscreen = false;
313
    bool ShowAppLongText = false;
314
    bool ShowAppWindowTitles = false;
315
316
    // Dear ImGui Tools (accessible from the "Tools" menu)
317
    bool ShowMetrics = false;
318
    bool ShowDebugLog = false;
319
    bool ShowIDStackTool = false;
320
    bool ShowStyleEditor = false;
321
    bool ShowAbout = false;
322
323
    // Other data
324
    bool DisableSections = false;
325
    ExampleTreeNode* DemoTree = NULL;
326
327
0
    ~ImGuiDemoWindowData() { if (DemoTree) ExampleTree_DestroyNode(DemoTree); }
328
};
329
330
// Demonstrate most Dear ImGui features (this is big function!)
331
// You may execute this function to experiment with the UI and understand what it does.
332
// You may then search for keywords in the code when you are interested by a specific feature.
333
void ImGui::ShowDemoWindow(bool* p_open)
334
0
{
335
    // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup
336
    // Most functions would normally just assert/crash if the context is missing.
337
0
    IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!");
338
339
    // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues.
340
0
    IMGUI_CHECKVERSION();
341
342
    // Stored data
343
0
    static ImGuiDemoWindowData demo_data;
344
345
    // Examples Apps (accessible from the "Examples" menu)
346
0
    if (demo_data.ShowMainMenuBar)          { ShowExampleAppMainMenuBar(); }
347
0
    if (demo_data.ShowAppDocuments)         { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); }
348
0
    if (demo_data.ShowAppAssetsBrowser)     { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); }
349
0
    if (demo_data.ShowAppConsole)           { ShowExampleAppConsole(&demo_data.ShowAppConsole); }
350
0
    if (demo_data.ShowAppCustomRendering)   { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); }
351
0
    if (demo_data.ShowAppLog)               { ShowExampleAppLog(&demo_data.ShowAppLog); }
352
0
    if (demo_data.ShowAppLayout)            { ShowExampleAppLayout(&demo_data.ShowAppLayout); }
353
0
    if (demo_data.ShowAppPropertyEditor)    { ShowExampleAppPropertyEditor(&demo_data.ShowAppPropertyEditor, &demo_data); }
354
0
    if (demo_data.ShowAppSimpleOverlay)     { ShowExampleAppSimpleOverlay(&demo_data.ShowAppSimpleOverlay); }
355
0
    if (demo_data.ShowAppAutoResize)        { ShowExampleAppAutoResize(&demo_data.ShowAppAutoResize); }
356
0
    if (demo_data.ShowAppConstrainedResize) { ShowExampleAppConstrainedResize(&demo_data.ShowAppConstrainedResize); }
357
0
    if (demo_data.ShowAppFullscreen)        { ShowExampleAppFullscreen(&demo_data.ShowAppFullscreen); }
358
0
    if (demo_data.ShowAppLongText)          { ShowExampleAppLongText(&demo_data.ShowAppLongText); }
359
0
    if (demo_data.ShowAppWindowTitles)      { ShowExampleAppWindowTitles(&demo_data.ShowAppWindowTitles); }
360
361
    // Dear ImGui Tools (accessible from the "Tools" menu)
362
0
    if (demo_data.ShowMetrics)              { ImGui::ShowMetricsWindow(&demo_data.ShowMetrics); }
363
0
    if (demo_data.ShowDebugLog)             { ImGui::ShowDebugLogWindow(&demo_data.ShowDebugLog); }
364
0
    if (demo_data.ShowIDStackTool)          { ImGui::ShowIDStackToolWindow(&demo_data.ShowIDStackTool); }
365
0
    if (demo_data.ShowAbout)                { ImGui::ShowAboutWindow(&demo_data.ShowAbout); }
366
0
    if (demo_data.ShowStyleEditor)
367
0
    {
368
0
        ImGui::Begin("Dear ImGui Style Editor", &demo_data.ShowStyleEditor);
369
0
        ImGui::ShowStyleEditor();
370
0
        ImGui::End();
371
0
    }
372
373
    // Demonstrate the various window flags. Typically you would just use the default!
374
0
    static bool no_titlebar = false;
375
0
    static bool no_scrollbar = false;
376
0
    static bool no_menu = false;
377
0
    static bool no_move = false;
378
0
    static bool no_resize = false;
379
0
    static bool no_collapse = false;
380
0
    static bool no_close = false;
381
0
    static bool no_nav = false;
382
0
    static bool no_background = false;
383
0
    static bool no_bring_to_front = false;
384
0
    static bool unsaved_document = false;
385
386
0
    ImGuiWindowFlags window_flags = 0;
387
0
    if (no_titlebar)        window_flags |= ImGuiWindowFlags_NoTitleBar;
388
0
    if (no_scrollbar)       window_flags |= ImGuiWindowFlags_NoScrollbar;
389
0
    if (!no_menu)           window_flags |= ImGuiWindowFlags_MenuBar;
390
0
    if (no_move)            window_flags |= ImGuiWindowFlags_NoMove;
391
0
    if (no_resize)          window_flags |= ImGuiWindowFlags_NoResize;
392
0
    if (no_collapse)        window_flags |= ImGuiWindowFlags_NoCollapse;
393
0
    if (no_nav)             window_flags |= ImGuiWindowFlags_NoNav;
394
0
    if (no_background)      window_flags |= ImGuiWindowFlags_NoBackground;
395
0
    if (no_bring_to_front)  window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;
396
0
    if (unsaved_document)   window_flags |= ImGuiWindowFlags_UnsavedDocument;
397
0
    if (no_close)           p_open = NULL; // Don't pass our bool* to Begin
398
399
    // We specify a default position/size in case there's no data in the .ini file.
400
    // We only do it to make the demo applications a little more welcoming, but typically this isn't required.
401
0
    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
402
0
    ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);
403
0
    ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);
404
405
    // Main body of the Demo window starts here.
406
0
    if (!ImGui::Begin("Dear ImGui Demo", p_open, window_flags))
407
0
    {
408
        // Early out if the window is collapsed, as an optimization.
409
0
        ImGui::End();
410
0
        return;
411
0
    }
412
413
    // Most framed widgets share a common width settings. Remaining width is used for the label.
414
    // The width of the frame may be changed with PushItemWidth() or SetNextItemWidth().
415
    // - Positive value for absolute size, negative value for right-alignment.
416
    // - The default value is about GetWindowWidth() * 0.65f.
417
    // - See 'Demo->Layout->Widgets Width' for details.
418
    // Here we change the frame width based on how much width we want to give to the label.
419
0
    const float label_width_base = ImGui::GetFontSize() * 12;               // Some amount of width for label, based on font size.
420
0
    const float label_width_max = ImGui::GetContentRegionAvail().x * 0.40f; // ...but always leave some room for framed widgets.
421
0
    const float label_width = IM_MIN(label_width_base, label_width_max);
422
0
    ImGui::PushItemWidth(-label_width);                                     // Right-align: framed items will leave 'label_width' available for the label.
423
    //ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.40f);       // e.g. Use 40% width for framed widgets, leaving 60% width for labels.
424
    //ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.40f);      // e.g. Use 40% width for labels, leaving 60% width for framed widgets.
425
    //ImGui::PushItemWidth(ImGui::GetFontSize() * -12);                     // e.g. Use XXX width for labels, leaving the rest for framed widgets.
426
427
    // Menu Bar
428
0
    DemoWindowMenuBar(&demo_data);
429
430
0
    ImGui::Text("dear imgui says hello! (%s) (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
431
0
    ImGui::Spacing();
432
433
0
    IMGUI_DEMO_MARKER("Help");
434
0
    if (ImGui::CollapsingHeader("Help"))
435
0
    {
436
0
        ImGui::SeparatorText("ABOUT THIS DEMO:");
437
0
        ImGui::BulletText("Sections below are demonstrating many aspects of the library.");
438
0
        ImGui::BulletText("The \"Examples\" menu above leads to more demo contents.");
439
0
        ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n"
440
0
                          "and Metrics/Debugger (general purpose Dear ImGui debugging tool).");
441
442
0
        ImGui::SeparatorText("PROGRAMMER GUIDE:");
443
0
        ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!");
444
0
        ImGui::BulletText("See comments in imgui.cpp.");
445
0
        ImGui::BulletText("See example applications in the examples/ folder.");
446
0
        ImGui::BulletText("Read the FAQ at ");
447
0
        ImGui::SameLine(0, 0);
448
0
        ImGui::TextLinkOpenURL("https://www.dearimgui.com/faq/");
449
0
        ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.");
450
0
        ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.");
451
452
0
        ImGui::SeparatorText("USER GUIDE:");
453
0
        ImGui::ShowUserGuide();
454
0
    }
455
456
0
    IMGUI_DEMO_MARKER("Configuration");
457
0
    if (ImGui::CollapsingHeader("Configuration"))
458
0
    {
459
0
        ImGuiIO& io = ImGui::GetIO();
460
461
0
        if (ImGui::TreeNode("Configuration##2"))
462
0
        {
463
0
            ImGui::SeparatorText("General");
464
0
            ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard",    &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);
465
0
            ImGui::SameLine(); HelpMarker("Enable keyboard controls.");
466
0
            ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad",     &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);
467
0
            ImGui::SameLine(); HelpMarker("Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\n\nRead instructions in imgui.cpp for details.");
468
0
            ImGui::CheckboxFlags("io.ConfigFlags: NoMouse",              &io.ConfigFlags, ImGuiConfigFlags_NoMouse);
469
0
            ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable mouse inputs and interactions.");
470
471
            // The "NoMouse" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:
472
0
            if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)
473
0
            {
474
0
                if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)
475
0
                {
476
0
                    ImGui::SameLine();
477
0
                    ImGui::Text("<<PRESS SPACE TO DISABLE>>");
478
0
                }
479
                // Prevent both being checked
480
0
                if (ImGui::IsKeyPressed(ImGuiKey_Space) || (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard))
481
0
                    io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
482
0
            }
483
484
0
            ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange",  &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);
485
0
            ImGui::SameLine(); HelpMarker("Instruct backend to not alter mouse cursor shape and visibility.");
486
0
            ImGui::CheckboxFlags("io.ConfigFlags: NoKeyboard", &io.ConfigFlags, ImGuiConfigFlags_NoKeyboard);
487
0
            ImGui::SameLine(); HelpMarker("Instruct dear imgui to disable keyboard inputs and interactions.");
488
489
0
            ImGui::Checkbox("io.ConfigInputTrickleEventQueue", &io.ConfigInputTrickleEventQueue);
490
0
            ImGui::SameLine(); HelpMarker("Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.");
491
0
            ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor);
492
0
            ImGui::SameLine(); HelpMarker("Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).");
493
494
0
            ImGui::SeparatorText("Keyboard/Gamepad Navigation");
495
0
            ImGui::Checkbox("io.ConfigNavSwapGamepadButtons", &io.ConfigNavSwapGamepadButtons);
496
0
            ImGui::Checkbox("io.ConfigNavMoveSetMousePos", &io.ConfigNavMoveSetMousePos);
497
0
            ImGui::SameLine(); HelpMarker("Directional/tabbing navigation teleports the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is difficult");
498
0
            ImGui::Checkbox("io.ConfigNavCaptureKeyboard", &io.ConfigNavCaptureKeyboard);
499
0
            ImGui::Checkbox("io.ConfigNavEscapeClearFocusItem", &io.ConfigNavEscapeClearFocusItem);
500
0
            ImGui::SameLine(); HelpMarker("Pressing Escape clears focused item.");
501
0
            ImGui::Checkbox("io.ConfigNavEscapeClearFocusWindow", &io.ConfigNavEscapeClearFocusWindow);
502
0
            ImGui::SameLine(); HelpMarker("Pressing Escape clears focused window.");
503
0
            ImGui::Checkbox("io.ConfigNavCursorVisibleAuto", &io.ConfigNavCursorVisibleAuto);
504
0
            ImGui::SameLine(); HelpMarker("Using directional navigation key makes the cursor visible. Mouse click hides the cursor.");
505
0
            ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways);
506
0
            ImGui::SameLine(); HelpMarker("Navigation cursor is always visible.");
507
508
0
            ImGui::SeparatorText("Windows");
509
0
            ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges);
510
0
            ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback.");
511
0
            ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly);
512
0
            ImGui::Checkbox("io.ConfigWindowsCopyContentsWithCtrlC", &io.ConfigWindowsCopyContentsWithCtrlC); // [EXPERIMENTAL]
513
0
            ImGui::SameLine(); HelpMarker("*EXPERIMENTAL* CTRL+C copy the contents of focused window into the clipboard.\n\nExperimental because:\n- (1) has known issues with nested Begin/End pairs.\n- (2) text output quality varies.\n- (3) text output is in submission order rather than spatial order.");
514
0
            ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage);
515
0
            ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location.");
516
517
0
            ImGui::SeparatorText("Widgets");
518
0
            ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink);
519
0
            ImGui::SameLine(); HelpMarker("Enable blinking cursor (optional as some users consider it to be distracting).");
520
0
            ImGui::Checkbox("io.ConfigInputTextEnterKeepActive", &io.ConfigInputTextEnterKeepActive);
521
0
            ImGui::SameLine(); HelpMarker("Pressing Enter will keep item active and select contents (single-line only).");
522
0
            ImGui::Checkbox("io.ConfigDragClickToInputText", &io.ConfigDragClickToInputText);
523
0
            ImGui::SameLine(); HelpMarker("Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).");
524
0
            ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors);
525
0
            ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors.");
526
0
            ImGui::Text("Also see Style->Rendering for rendering options.");
527
528
            // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling
529
0
            ImGui::SeparatorText("Error Handling");
530
531
0
            ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery);
532
0
            ImGui::SameLine(); HelpMarker(
533
0
                "Options to configure how we handle recoverable errors.\n"
534
0
                "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n"
535
0
                "- You not are not supposed to rely on it in the course of a normal application run.\n"
536
0
                "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n"
537
0
                "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call! "
538
0
                "Otherwise it would severely hinder your ability to catch and correct mistakes!");
539
0
            ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert);
540
0
            ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog);
541
0
            ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip);
542
0
            if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip)
543
0
                io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true;
544
545
            // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools
546
0
            ImGui::SeparatorText("Debug");
547
0
            ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
548
0
            ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
549
0
            ImGui::Checkbox("io.ConfigDebugHighlightIdConflicts", &io.ConfigDebugHighlightIdConflicts);
550
0
            ImGui::SameLine(); HelpMarker("Highlight and show an error message when multiple items have conflicting identifiers.");
551
0
            ImGui::BeginDisabled();
552
0
            ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce);
553
0
            ImGui::EndDisabled();
554
0
            ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.");
555
0
            ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop);
556
0
            ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
557
0
            ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss);
558
0
            ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.");
559
0
            ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings);
560
0
            ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).");
561
562
0
            ImGui::TreePop();
563
0
            ImGui::Spacing();
564
0
        }
565
566
0
        IMGUI_DEMO_MARKER("Configuration/Backend Flags");
567
0
        if (ImGui::TreeNode("Backend Flags"))
568
0
        {
569
0
            HelpMarker(
570
0
                "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n"
571
0
                "Here we expose them as read-only fields to avoid breaking interactions with your backend.");
572
573
            // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?
574
0
            ImGui::BeginDisabled();
575
0
            ImGui::CheckboxFlags("io.BackendFlags: HasGamepad",           &io.BackendFlags, ImGuiBackendFlags_HasGamepad);
576
0
            ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors",      &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
577
0
            ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos",       &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);
578
0
            ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);
579
0
            ImGui::CheckboxFlags("io.BackendFlags: RendererHasTextures",  &io.BackendFlags, ImGuiBackendFlags_RendererHasTextures);
580
0
            ImGui::EndDisabled();
581
582
0
            ImGui::TreePop();
583
0
            ImGui::Spacing();
584
0
        }
585
586
0
        IMGUI_DEMO_MARKER("Configuration/Style, Fonts");
587
0
        if (ImGui::TreeNode("Style, Fonts"))
588
0
        {
589
0
            ImGui::Checkbox("Style Editor", &demo_data.ShowStyleEditor);
590
0
            ImGui::SameLine();
591
0
            HelpMarker("The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.");
592
0
            ImGui::TreePop();
593
0
            ImGui::Spacing();
594
0
        }
595
596
0
        IMGUI_DEMO_MARKER("Configuration/Capture, Logging");
597
0
        if (ImGui::TreeNode("Capture/Logging"))
598
0
        {
599
0
            HelpMarker(
600
0
                "The logging API redirects all text output so you can easily capture the content of "
601
0
                "a window or a block. Tree nodes can be automatically expanded.\n"
602
0
                "Try opening any of the contents below in this window and then click one of the \"Log To\" button.");
603
0
            ImGui::LogButtons();
604
605
0
            HelpMarker("You can also call ImGui::LogText() to output directly to the log without a visual output.");
606
0
            if (ImGui::Button("Copy \"Hello, world!\" to clipboard"))
607
0
            {
608
0
                ImGui::LogToClipboard();
609
0
                ImGui::LogText("Hello, world!");
610
0
                ImGui::LogFinish();
611
0
            }
612
0
            ImGui::TreePop();
613
0
        }
614
0
    }
615
616
0
    IMGUI_DEMO_MARKER("Window options");
617
0
    if (ImGui::CollapsingHeader("Window options"))
618
0
    {
619
0
        if (ImGui::BeginTable("split", 3))
620
0
        {
621
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No titlebar", &no_titlebar);
622
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No scrollbar", &no_scrollbar);
623
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No menu", &no_menu);
624
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No move", &no_move);
625
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No resize", &no_resize);
626
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No collapse", &no_collapse);
627
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No close", &no_close);
628
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav);
629
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background);
630
0
            ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front);
631
0
            ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document);
632
0
            ImGui::EndTable();
633
0
        }
634
0
    }
635
636
    // All demo contents
637
0
    DemoWindowWidgets(&demo_data);
638
0
    DemoWindowLayout();
639
0
    DemoWindowPopups();
640
0
    DemoWindowTables();
641
0
    DemoWindowInputs();
642
643
    // End of ShowDemoWindow()
644
0
    ImGui::PopItemWidth();
645
0
    ImGui::End();
646
0
}
647
648
//-----------------------------------------------------------------------------
649
// [SECTION] DemoWindowMenuBar()
650
//-----------------------------------------------------------------------------
651
652
static void DemoWindowMenuBar(ImGuiDemoWindowData* demo_data)
653
0
{
654
0
    IMGUI_DEMO_MARKER("Menu");
655
0
    if (ImGui::BeginMenuBar())
656
0
    {
657
0
        if (ImGui::BeginMenu("Menu"))
658
0
        {
659
0
            IMGUI_DEMO_MARKER("Menu/File");
660
0
            ShowExampleMenuFile();
661
0
            ImGui::EndMenu();
662
0
        }
663
0
        if (ImGui::BeginMenu("Examples"))
664
0
        {
665
0
            IMGUI_DEMO_MARKER("Menu/Examples");
666
0
            ImGui::MenuItem("Main menu bar", NULL, &demo_data->ShowMainMenuBar);
667
668
0
            ImGui::SeparatorText("Mini apps");
669
0
            ImGui::MenuItem("Assets Browser", NULL, &demo_data->ShowAppAssetsBrowser);
670
0
            ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole);
671
0
            ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering);
672
0
            ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments);
673
0
            ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog);
674
0
            ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor);
675
0
            ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout);
676
0
            ImGui::MenuItem("Simple overlay", NULL, &demo_data->ShowAppSimpleOverlay);
677
678
0
            ImGui::SeparatorText("Concepts");
679
0
            ImGui::MenuItem("Auto-resizing window", NULL, &demo_data->ShowAppAutoResize);
680
0
            ImGui::MenuItem("Constrained-resizing window", NULL, &demo_data->ShowAppConstrainedResize);
681
0
            ImGui::MenuItem("Fullscreen window", NULL, &demo_data->ShowAppFullscreen);
682
0
            ImGui::MenuItem("Long text display", NULL, &demo_data->ShowAppLongText);
683
0
            ImGui::MenuItem("Manipulating window titles", NULL, &demo_data->ShowAppWindowTitles);
684
685
0
            ImGui::EndMenu();
686
0
        }
687
        //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar!
688
0
        if (ImGui::BeginMenu("Tools"))
689
0
        {
690
0
            IMGUI_DEMO_MARKER("Menu/Tools");
691
0
            ImGuiIO& io = ImGui::GetIO();
692
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
693
0
            const bool has_debug_tools = true;
694
#else
695
            const bool has_debug_tools = false;
696
#endif
697
0
            ImGui::MenuItem("Metrics/Debugger", NULL, &demo_data->ShowMetrics, has_debug_tools);
698
0
            if (ImGui::BeginMenu("Debug Options"))
699
0
            {
700
0
                ImGui::BeginDisabled(!has_debug_tools);
701
0
                ImGui::Checkbox("Highlight ID Conflicts", &io.ConfigDebugHighlightIdConflicts);
702
0
                ImGui::EndDisabled();
703
0
                ImGui::Checkbox("Assert on error recovery", &io.ConfigErrorRecoveryEnableAssert);
704
0
                ImGui::TextDisabled("(see Demo->Configuration for details & more)");
705
0
                ImGui::EndMenu();
706
0
            }
707
0
            ImGui::MenuItem("Debug Log", NULL, &demo_data->ShowDebugLog, has_debug_tools);
708
0
            ImGui::MenuItem("ID Stack Tool", NULL, &demo_data->ShowIDStackTool, has_debug_tools);
709
0
            bool is_debugger_present = io.ConfigDebugIsDebuggerPresent;
710
0
            if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools))// && is_debugger_present))
711
0
                ImGui::DebugStartItemPicker();
712
0
            if (!is_debugger_present)
713
0
                ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable some extra features to avoid casual users crashing the application.");
714
0
            ImGui::MenuItem("Style Editor", NULL, &demo_data->ShowStyleEditor);
715
0
            ImGui::MenuItem("About Dear ImGui", NULL, &demo_data->ShowAbout);
716
717
0
            ImGui::EndMenu();
718
0
        }
719
0
        ImGui::EndMenuBar();
720
0
    }
721
0
}
722
723
//-----------------------------------------------------------------------------
724
// [SECTION] Helpers: ExampleTreeNode, ExampleMemberInfo (for use by Property Editor & Multi-Select demos)
725
//-----------------------------------------------------------------------------
726
727
// Simple representation for a tree
728
// (this is designed to be simple to understand for our demos, not to be fancy or efficient etc.)
729
struct ExampleTreeNode
730
{
731
    // Tree structure
732
    char                        Name[28] = "";
733
    int                         UID = 0;
734
    ExampleTreeNode* Parent = NULL;
735
    ImVector<ExampleTreeNode*>  Childs;
736
    unsigned short              IndexInParent = 0;  // Maintaining this allows us to implement linear traversal more easily
737
738
    // Leaf Data
739
    bool                        HasData = false;    // All leaves have data
740
    bool                        DataMyBool = true;
741
    int                         DataMyInt = 128;
742
    ImVec2                      DataMyVec2 = ImVec2(0.0f, 3.141592f);
743
};
744
745
// Simple representation of struct metadata/serialization data.
746
// (this is a minimal version of what a typical advanced application may provide)
747
struct ExampleMemberInfo
748
{
749
    const char* Name;       // Member name
750
    ImGuiDataType   DataType;   // Member type
751
    int             DataCount;  // Member count (1 when scalar)
752
    int             Offset;     // Offset inside parent structure
753
};
754
755
// Metadata description of ExampleTreeNode struct.
756
static const ExampleMemberInfo ExampleTreeNodeMemberInfos[]
757
{
758
    { "MyName",     ImGuiDataType_String,  1, offsetof(ExampleTreeNode, Name) },
759
    { "MyBool",     ImGuiDataType_Bool,    1, offsetof(ExampleTreeNode, DataMyBool) },
760
    { "MyInt",      ImGuiDataType_S32,     1, offsetof(ExampleTreeNode, DataMyInt) },
761
    { "MyVec2",     ImGuiDataType_Float,   2, offsetof(ExampleTreeNode, DataMyVec2) },
762
};
763
764
static ExampleTreeNode* ExampleTree_CreateNode(const char* name, int uid, ExampleTreeNode* parent)
765
0
{
766
0
    ExampleTreeNode* node = IM_NEW(ExampleTreeNode);
767
0
    snprintf(node->Name, IM_ARRAYSIZE(node->Name), "%s", name);
768
0
    node->UID = uid;
769
0
    node->Parent = parent;
770
0
    node->IndexInParent = parent ? (unsigned short)parent->Childs.Size : 0;
771
0
    if (parent)
772
0
        parent->Childs.push_back(node);
773
0
    return node;
774
0
}
775
776
static void ExampleTree_DestroyNode(ExampleTreeNode* node)
777
0
{
778
0
    for (ExampleTreeNode* child_node : node->Childs)
779
0
        ExampleTree_DestroyNode(child_node);
780
0
    IM_DELETE(node);
781
0
}
782
783
// Create example tree data
784
// (this allocates _many_ more times than most other code in either Dear ImGui or others demo)
785
static ExampleTreeNode* ExampleTree_CreateDemoTree()
786
0
{
787
0
    static const char* root_names[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pear", "Pineapple", "Strawberry", "Watermelon" };
788
0
    const size_t NAME_MAX_LEN = sizeof(ExampleTreeNode::Name);
789
0
    char name_buf[NAME_MAX_LEN];
790
0
    int uid = 0;
791
0
    ExampleTreeNode* node_L0 = ExampleTree_CreateNode("<ROOT>", ++uid, NULL);
792
0
    const int root_items_multiplier = 2;
793
0
    for (int idx_L0 = 0; idx_L0 < IM_ARRAYSIZE(root_names) * root_items_multiplier; idx_L0++)
794
0
    {
795
0
        snprintf(name_buf, IM_ARRAYSIZE(name_buf), "%s %d", root_names[idx_L0 / root_items_multiplier], idx_L0 % root_items_multiplier);
796
0
        ExampleTreeNode* node_L1 = ExampleTree_CreateNode(name_buf, ++uid, node_L0);
797
0
        const int number_of_childs = (int)strlen(node_L1->Name);
798
0
        for (int idx_L1 = 0; idx_L1 < number_of_childs; idx_L1++)
799
0
        {
800
0
            snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Child %d", idx_L1);
801
0
            ExampleTreeNode* node_L2 = ExampleTree_CreateNode(name_buf, ++uid, node_L1);
802
0
            node_L2->HasData = true;
803
0
            if (idx_L1 == 0)
804
0
            {
805
0
                snprintf(name_buf, IM_ARRAYSIZE(name_buf), "Sub-child %d", 0);
806
0
                ExampleTreeNode* node_L3 = ExampleTree_CreateNode(name_buf, ++uid, node_L2);
807
0
                node_L3->HasData = true;
808
0
            }
809
0
        }
810
0
    }
811
0
    return node_L0;
812
0
}
813
814
//-----------------------------------------------------------------------------
815
// [SECTION] DemoWindowWidgetsBasic()
816
//-----------------------------------------------------------------------------
817
818
static void DemoWindowWidgetsBasic()
819
0
{
820
0
    IMGUI_DEMO_MARKER("Widgets/Basic");
821
0
    if (ImGui::TreeNode("Basic"))
822
0
    {
823
0
        ImGui::SeparatorText("General");
824
825
0
        IMGUI_DEMO_MARKER("Widgets/Basic/Button");
826
0
        static int clicked = 0;
827
0
        if (ImGui::Button("Button"))
828
0
            clicked++;
829
0
        if (clicked & 1)
830
0
        {
831
0
            ImGui::SameLine();
832
0
            ImGui::Text("Thanks for clicking me!");
833
0
        }
834
835
0
        IMGUI_DEMO_MARKER("Widgets/Basic/Checkbox");
836
0
        static bool check = true;
837
0
        ImGui::Checkbox("checkbox", &check);
838
839
0
        IMGUI_DEMO_MARKER("Widgets/Basic/RadioButton");
840
0
        static int e = 0;
841
0
        ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine();
842
0
        ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine();
843
0
        ImGui::RadioButton("radio c", &e, 2);
844
845
0
        ImGui::AlignTextToFramePadding();
846
0
        ImGui::TextLinkOpenURL("Hyperlink", "https://github.com/ocornut/imgui/wiki/Error-Handling");
847
848
        // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.
849
0
        IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Colored)");
850
0
        for (int i = 0; i < 7; i++)
851
0
        {
852
0
            if (i > 0)
853
0
                ImGui::SameLine();
854
0
            ImGui::PushID(i);
855
0
            ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));
856
0
            ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));
857
0
            ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));
858
0
            ImGui::Button("Click");
859
0
            ImGui::PopStyleColor(3);
860
0
            ImGui::PopID();
861
0
        }
862
863
        // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements
864
        // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)
865
        // See 'Demo->Layout->Text Baseline Alignment' for details.
866
0
        ImGui::AlignTextToFramePadding();
867
0
        ImGui::Text("Hold to repeat:");
868
0
        ImGui::SameLine();
869
870
        // Arrow buttons with Repeater
871
0
        IMGUI_DEMO_MARKER("Widgets/Basic/Buttons (Repeating)");
872
0
        static int counter = 0;
873
0
        float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
874
0
        ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
875
0
        if (ImGui::ArrowButton("##left", ImGuiDir_Left)) { counter--; }
876
0
        ImGui::SameLine(0.0f, spacing);
877
0
        if (ImGui::ArrowButton("##right", ImGuiDir_Right)) { counter++; }
878
0
        ImGui::PopItemFlag();
879
0
        ImGui::SameLine();
880
0
        ImGui::Text("%d", counter);
881
882
0
        ImGui::Button("Tooltip");
883
0
        ImGui::SetItemTooltip("I am a tooltip");
884
885
0
        ImGui::LabelText("label", "Value");
886
887
0
        ImGui::SeparatorText("Inputs");
888
889
0
        {
890
            // To wire InputText() with std::string or any other custom string type,
891
            // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
892
0
            IMGUI_DEMO_MARKER("Widgets/Basic/InputText");
893
0
            static char str0[128] = "Hello, world!";
894
0
            ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0));
895
0
            ImGui::SameLine(); HelpMarker(
896
0
                "USER:\n"
897
0
                "Hold SHIFT or use mouse to select text.\n"
898
0
                "CTRL+Left/Right to word jump.\n"
899
0
                "CTRL+A or Double-Click to select all.\n"
900
0
                "CTRL+X,CTRL+C,CTRL+V for clipboard.\n"
901
0
                "CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.\n"
902
0
                "ESCAPE to revert.\n\n"
903
0
                "PROGRAMMER:\n"
904
0
                "You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() "
905
0
                "to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated "
906
0
                "in imgui_demo.cpp).");
907
908
0
            static char str1[128] = "";
909
0
            ImGui::InputTextWithHint("input text (w/ hint)", "enter text here", str1, IM_ARRAYSIZE(str1));
910
911
0
            IMGUI_DEMO_MARKER("Widgets/Basic/InputInt, InputFloat");
912
0
            static int i0 = 123;
913
0
            ImGui::InputInt("input int", &i0);
914
915
0
            static float f0 = 0.001f;
916
0
            ImGui::InputFloat("input float", &f0, 0.01f, 1.0f, "%.3f");
917
918
0
            static double d0 = 999999.00000001;
919
0
            ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f");
920
921
0
            static float f1 = 1.e10f;
922
0
            ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e");
923
0
            ImGui::SameLine(); HelpMarker(
924
0
                "You can input value using the scientific notation,\n"
925
0
                "  e.g. \"1e+8\" becomes \"100000000\".");
926
927
0
            static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
928
0
            ImGui::InputFloat3("input float3", vec4a);
929
0
        }
930
931
0
        ImGui::SeparatorText("Drags");
932
933
0
        {
934
0
            IMGUI_DEMO_MARKER("Widgets/Basic/DragInt, DragFloat");
935
0
            static int i1 = 50, i2 = 42, i3 = 128;
936
0
            ImGui::DragInt("drag int", &i1, 1);
937
0
            ImGui::SameLine(); HelpMarker(
938
0
                "Click and drag to edit value.\n"
939
0
                "Hold SHIFT/ALT for faster/slower edit.\n"
940
0
                "Double-click or CTRL+click to input value.");
941
0
            ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp);
942
0
            ImGui::DragInt("drag int wrap 100..200", &i3, 1, 100, 200, "%d", ImGuiSliderFlags_WrapAround);
943
944
0
            static float f1 = 1.00f, f2 = 0.0067f;
945
0
            ImGui::DragFloat("drag float", &f1, 0.005f);
946
0
            ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns");
947
            //ImGui::DragFloat("drag wrap -1..1", &f3, 0.005f, -1.0f, 1.0f, NULL, ImGuiSliderFlags_WrapAround);
948
0
        }
949
950
0
        ImGui::SeparatorText("Sliders");
951
952
0
        {
953
0
            IMGUI_DEMO_MARKER("Widgets/Basic/SliderInt, SliderFloat");
954
0
            static int i1 = 0;
955
0
            ImGui::SliderInt("slider int", &i1, -1, 3);
956
0
            ImGui::SameLine(); HelpMarker("CTRL+click to input value.");
957
958
0
            static float f1 = 0.123f, f2 = 0.0f;
959
0
            ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f");
960
0
            ImGui::SliderFloat("slider float (log)", &f2, -10.0f, 10.0f, "%.4f", ImGuiSliderFlags_Logarithmic);
961
962
0
            IMGUI_DEMO_MARKER("Widgets/Basic/SliderAngle");
963
0
            static float angle = 0.0f;
964
0
            ImGui::SliderAngle("slider angle", &angle);
965
966
            // Using the format string to display a name instead of an integer.
967
            // Here we completely omit '%d' from the format string, so it'll only display a name.
968
            // This technique can also be used with DragInt().
969
0
            IMGUI_DEMO_MARKER("Widgets/Basic/Slider (enum)");
970
0
            enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };
971
0
            static int elem = Element_Fire;
972
0
            const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" };
973
0
            const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown";
974
0
            ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here.
975
0
            ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer.");
976
0
        }
977
978
0
        ImGui::SeparatorText("Selectors/Pickers");
979
980
0
        {
981
0
            IMGUI_DEMO_MARKER("Widgets/Basic/ColorEdit3, ColorEdit4");
982
0
            static float col1[3] = { 1.0f, 0.0f, 0.2f };
983
0
            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
984
0
            ImGui::ColorEdit3("color 1", col1);
985
0
            ImGui::SameLine(); HelpMarker(
986
0
                "Click on the color square to open a color picker.\n"
987
0
                "Click and hold to use drag and drop.\n"
988
0
                "Right-click on the color square to show options.\n"
989
0
                "CTRL+click on individual component to input value.\n");
990
991
0
            ImGui::ColorEdit4("color 2", col2);
992
0
        }
993
994
0
        {
995
            // Using the _simplified_ one-liner Combo() api here
996
            // See "Combo" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.
997
0
            IMGUI_DEMO_MARKER("Widgets/Basic/Combo");
998
0
            const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIIIIII", "JJJJ", "KKKKKKK" };
999
0
            static int item_current = 0;
1000
0
            ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items));
1001
0
            ImGui::SameLine(); HelpMarker(
1002
0
                "Using the simplified one-liner Combo API here.\n"
1003
0
                "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.");
1004
0
        }
1005
1006
0
        {
1007
            // Using the _simplified_ one-liner ListBox() api here
1008
            // See "List boxes" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.
1009
0
            IMGUI_DEMO_MARKER("Widgets/Basic/ListBox");
1010
0
            const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" };
1011
0
            static int item_current = 1;
1012
0
            ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4);
1013
0
            ImGui::SameLine(); HelpMarker(
1014
0
                "Using the simplified one-liner ListBox API here.\n"
1015
0
                "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.");
1016
0
        }
1017
1018
        // Testing ImGuiOnceUponAFrame helper.
1019
        //static ImGuiOnceUponAFrame once;
1020
        //for (int i = 0; i < 5; i++)
1021
        //    if (once)
1022
        //        ImGui::Text("This will be displayed only once.");
1023
1024
0
        ImGui::TreePop();
1025
0
    }
1026
0
}
1027
1028
//-----------------------------------------------------------------------------
1029
// [SECTION] DemoWindowWidgetsBullets()
1030
//-----------------------------------------------------------------------------
1031
1032
static void DemoWindowWidgetsBullets()
1033
0
{
1034
0
    IMGUI_DEMO_MARKER("Widgets/Bullets");
1035
0
    if (ImGui::TreeNode("Bullets"))
1036
0
    {
1037
0
        ImGui::BulletText("Bullet point 1");
1038
0
        ImGui::BulletText("Bullet point 2\nOn multiple lines");
1039
0
        if (ImGui::TreeNode("Tree node"))
1040
0
        {
1041
0
            ImGui::BulletText("Another bullet point");
1042
0
            ImGui::TreePop();
1043
0
        }
1044
0
        ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)");
1045
0
        ImGui::Bullet(); ImGui::SmallButton("Button");
1046
0
        ImGui::TreePop();
1047
0
    }
1048
0
}
1049
1050
//-----------------------------------------------------------------------------
1051
// [SECTION] DemoWindowWidgetsCollapsingHeaders()
1052
//-----------------------------------------------------------------------------
1053
1054
static void DemoWindowWidgetsCollapsingHeaders()
1055
0
{
1056
0
    IMGUI_DEMO_MARKER("Widgets/Collapsing Headers");
1057
0
    if (ImGui::TreeNode("Collapsing Headers"))
1058
0
    {
1059
0
        static bool closable_group = true;
1060
0
        ImGui::Checkbox("Show 2nd header", &closable_group);
1061
0
        if (ImGui::CollapsingHeader("Header", ImGuiTreeNodeFlags_None))
1062
0
        {
1063
0
            ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1064
0
            for (int i = 0; i < 5; i++)
1065
0
                ImGui::Text("Some content %d", i);
1066
0
        }
1067
0
        if (ImGui::CollapsingHeader("Header with a close button", &closable_group))
1068
0
        {
1069
0
            ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1070
0
            for (int i = 0; i < 5; i++)
1071
0
                ImGui::Text("More content %d", i);
1072
0
        }
1073
        /*
1074
        if (ImGui::CollapsingHeader("Header with a bullet", ImGuiTreeNodeFlags_Bullet))
1075
            ImGui::Text("IsItemHovered: %d", ImGui::IsItemHovered());
1076
        */
1077
0
        ImGui::TreePop();
1078
0
    }
1079
0
}
1080
1081
//-----------------------------------------------------------------------------
1082
// [SECTION] DemoWindowWidgetsColorAndPickers()
1083
//-----------------------------------------------------------------------------
1084
1085
static void DemoWindowWidgetsColorAndPickers()
1086
0
{
1087
0
    IMGUI_DEMO_MARKER("Widgets/Color");
1088
0
    if (ImGui::TreeNode("Color/Picker Widgets"))
1089
0
    {
1090
0
        static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);
1091
0
        static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None;
1092
1093
0
        ImGui::SeparatorText("Options");
1094
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &base_flags, ImGuiColorEditFlags_NoAlpha);
1095
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque);
1096
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg);
1097
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf);
1098
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop);
1099
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options.");
1100
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets.");
1101
1102
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit");
1103
0
        ImGui::SeparatorText("Inline color editor");
1104
0
        ImGui::Text("Color widget:");
1105
0
        ImGui::SameLine(); HelpMarker(
1106
0
            "Click on the color square to open a color picker.\n"
1107
0
            "CTRL+click on individual component to input value.\n");
1108
0
        ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags);
1109
1110
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)");
1111
0
        ImGui::Text("Color widget HSV with Alpha:");
1112
0
        ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags);
1113
1114
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)");
1115
0
        ImGui::Text("Color widget with Float Display:");
1116
0
        ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | base_flags);
1117
1118
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)");
1119
0
        ImGui::Text("Color button with Picker:");
1120
0
        ImGui::SameLine(); HelpMarker(
1121
0
            "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n"
1122
0
            "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only "
1123
0
            "be used for the tooltip and picker popup.");
1124
0
        ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags);
1125
1126
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)");
1127
0
        ImGui::Text("Color button with Custom Picker Popup:");
1128
1129
        // Generate a default palette. The palette will persist and can be edited.
1130
0
        static bool saved_palette_init = true;
1131
0
        static ImVec4 saved_palette[32] = {};
1132
0
        if (saved_palette_init)
1133
0
        {
1134
0
            for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1135
0
            {
1136
0
                ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f,
1137
0
                    saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);
1138
0
                saved_palette[n].w = 1.0f; // Alpha
1139
0
            }
1140
0
            saved_palette_init = false;
1141
0
        }
1142
1143
0
        static ImVec4 backup_color;
1144
0
        bool open_popup = ImGui::ColorButton("MyColor##3b", color, base_flags);
1145
0
        ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
1146
0
        open_popup |= ImGui::Button("Palette");
1147
0
        if (open_popup)
1148
0
        {
1149
0
            ImGui::OpenPopup("mypicker");
1150
0
            backup_color = color;
1151
0
        }
1152
0
        if (ImGui::BeginPopup("mypicker"))
1153
0
        {
1154
0
            ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!");
1155
0
            ImGui::Separator();
1156
0
            ImGui::ColorPicker4("##picker", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);
1157
0
            ImGui::SameLine();
1158
1159
0
            ImGui::BeginGroup(); // Lock X position
1160
0
            ImGui::Text("Current");
1161
0
            ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40));
1162
0
            ImGui::Text("Previous");
1163
0
            if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))
1164
0
                color = backup_color;
1165
0
            ImGui::Separator();
1166
0
            ImGui::Text("Palette");
1167
0
            for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
1168
0
            {
1169
0
                ImGui::PushID(n);
1170
0
                if ((n % 8) != 0)
1171
0
                    ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
1172
1173
0
                ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;
1174
0
                if (ImGui::ColorButton("##palette", saved_palette[n], palette_button_flags, ImVec2(20, 20)))
1175
0
                    color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
1176
1177
                // Allow user to drop colors into each palette entry. Note that ColorButton() is already a
1178
                // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.
1179
0
                if (ImGui::BeginDragDropTarget())
1180
0
                {
1181
0
                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
1182
0
                        memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);
1183
0
                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
1184
0
                        memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);
1185
0
                    ImGui::EndDragDropTarget();
1186
0
                }
1187
1188
0
                ImGui::PopID();
1189
0
            }
1190
0
            ImGui::EndGroup();
1191
0
            ImGui::EndPopup();
1192
0
        }
1193
1194
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (simple)");
1195
0
        ImGui::Text("Color button only:");
1196
0
        static bool no_border = false;
1197
0
        ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border);
1198
0
        ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80));
1199
1200
0
        IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker");
1201
0
        ImGui::SeparatorText("Color picker");
1202
1203
0
        static bool ref_color = false;
1204
0
        static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);
1205
0
        static int picker_mode = 0;
1206
0
        static int display_mode = 0;
1207
0
        static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar;
1208
1209
0
        ImGui::PushID("Color picker");
1210
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &color_picker_flags, ImGuiColorEditFlags_NoAlpha);
1211
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaBar", &color_picker_flags, ImGuiColorEditFlags_AlphaBar);
1212
0
        ImGui::CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview);
1213
0
        if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview)
1214
0
        {
1215
0
            ImGui::SameLine();
1216
0
            ImGui::Checkbox("With Ref Color", &ref_color);
1217
0
            if (ref_color)
1218
0
            {
1219
0
                ImGui::SameLine();
1220
0
                ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags);
1221
0
            }
1222
0
        }
1223
1224
0
        ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0");
1225
0
        ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode.");
1226
1227
0
        ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0");
1228
0
        ImGui::SameLine(); HelpMarker(
1229
0
            "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, "
1230
0
            "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex "
1231
0
            "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions().");
1232
1233
0
        ImGuiColorEditFlags flags = base_flags | color_picker_flags;
1234
0
        if (picker_mode == 1)  flags |= ImGuiColorEditFlags_PickerHueBar;
1235
0
        if (picker_mode == 2)  flags |= ImGuiColorEditFlags_PickerHueWheel;
1236
0
        if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs;       // Disable all RGB/HSV/Hex displays
1237
0
        if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB;     // Override display mode
1238
0
        if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;
1239
0
        if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;
1240
0
        ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);
1241
1242
0
        ImGui::Text("Set defaults in code:");
1243
0
        ImGui::SameLine(); HelpMarker(
1244
0
            "SetColorEditOptions() is designed to allow you to set boot-time default.\n"
1245
0
            "We don't have Push/Pop functions because you can force options on a per-widget basis if needed, "
1246
0
            "and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid "
1247
0
            "encouraging you to persistently save values that aren't forward-compatible.");
1248
0
        if (ImGui::Button("Default: Uint8 + HSV + Hue Bar"))
1249
0
            ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);
1250
0
        if (ImGui::Button("Default: Float + HDR + Hue Wheel"))
1251
0
            ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);
1252
1253
        // Always display a small version of both types of pickers
1254
        // (that's in order to make it more visible in the demo to people who are skimming quickly through it)
1255
0
        ImGui::Text("Both types:");
1256
0
        float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;
1257
0
        ImGui::SetNextItemWidth(w);
1258
0
        ImGui::ColorPicker3("##MyColor##5", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1259
0
        ImGui::SameLine();
1260
0
        ImGui::SetNextItemWidth(w);
1261
0
        ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);
1262
0
        ImGui::PopID();
1263
1264
        // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)
1265
0
        static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!
1266
0
        ImGui::Spacing();
1267
0
        ImGui::Text("HSV encoded colors");
1268
0
        ImGui::SameLine(); HelpMarker(
1269
0
            "By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV "
1270
0
            "allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the "
1271
0
            "added benefit that you can manipulate hue values with the picker even when saturation or value are zero.");
1272
0
        ImGui::Text("Color widget with InputHSV:");
1273
0
        ImGui::ColorEdit4("HSV shown as RGB##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1274
0
        ImGui::ColorEdit4("HSV shown as HSV##1", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);
1275
0
        ImGui::DragFloat4("Raw HSV values", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);
1276
1277
0
        ImGui::TreePop();
1278
0
    }
1279
0
}
1280
1281
//-----------------------------------------------------------------------------
1282
// [SECTION] DemoWindowWidgetsComboBoxes()
1283
//-----------------------------------------------------------------------------
1284
1285
static void DemoWindowWidgetsComboBoxes()
1286
0
{
1287
0
    IMGUI_DEMO_MARKER("Widgets/Combo");
1288
0
    if (ImGui::TreeNode("Combo"))
1289
0
    {
1290
        // Combo Boxes are also called "Dropdown" in other systems
1291
        // Expose flags as checkbox for the demo
1292
0
        static ImGuiComboFlags flags = 0;
1293
0
        ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft);
1294
0
        ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo");
1295
0
        if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton))
1296
0
            flags &= ~ImGuiComboFlags_NoPreview;     // Clear incompatible flags
1297
0
        if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview))
1298
0
            flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags
1299
0
        if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview))
1300
0
            flags &= ~ImGuiComboFlags_NoPreview;
1301
1302
        // Override default popup height
1303
0
        if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightSmall", &flags, ImGuiComboFlags_HeightSmall))
1304
0
            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);
1305
0
        if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightRegular", &flags, ImGuiComboFlags_HeightRegular))
1306
0
            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);
1307
0
        if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightLargest", &flags, ImGuiComboFlags_HeightLargest))
1308
0
            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);
1309
1310
        // Using the generic BeginCombo() API, you have full control over how to display the combo contents.
1311
        // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1312
        // stored in the object itself, etc.)
1313
0
        const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1314
0
        static int item_selected_idx = 0; // Here we store our selection data as an index.
1315
1316
        // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[])
1317
0
        const char* combo_preview_value = items[item_selected_idx];
1318
0
        if (ImGui::BeginCombo("combo 1", combo_preview_value, flags))
1319
0
        {
1320
0
            for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1321
0
            {
1322
0
                const bool is_selected = (item_selected_idx == n);
1323
0
                if (ImGui::Selectable(items[n], is_selected))
1324
0
                    item_selected_idx = n;
1325
1326
                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1327
0
                if (is_selected)
1328
0
                    ImGui::SetItemDefaultFocus();
1329
0
            }
1330
0
            ImGui::EndCombo();
1331
0
        }
1332
1333
        // Show case embedding a filter using a simple trick: displaying the filter inside combo contents.
1334
        // See https://github.com/ocornut/imgui/issues/718 for advanced/esoteric alternatives.
1335
0
        if (ImGui::BeginCombo("combo 2 (w/ filter)", combo_preview_value, flags))
1336
0
        {
1337
0
            static ImGuiTextFilter filter;
1338
0
            if (ImGui::IsWindowAppearing())
1339
0
            {
1340
0
                ImGui::SetKeyboardFocusHere();
1341
0
                filter.Clear();
1342
0
            }
1343
0
            ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F);
1344
0
            filter.Draw("##Filter", -FLT_MIN);
1345
1346
0
            for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1347
0
            {
1348
0
                const bool is_selected = (item_selected_idx == n);
1349
0
                if (filter.PassFilter(items[n]))
1350
0
                    if (ImGui::Selectable(items[n], is_selected))
1351
0
                        item_selected_idx = n;
1352
0
            }
1353
0
            ImGui::EndCombo();
1354
0
        }
1355
1356
0
        ImGui::Spacing();
1357
0
        ImGui::SeparatorText("One-liner variants");
1358
0
        HelpMarker("The Combo() function is not greatly useful apart from cases were you want to embed all options in a single strings.\nFlags above don't apply to this section.");
1359
1360
        // Simplified one-liner Combo() API, using values packed in a single constant string
1361
        // This is a convenience for when the selection set is small and known at compile-time.
1362
0
        static int item_current_2 = 0;
1363
0
        ImGui::Combo("combo 3 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
1364
1365
        // Simplified one-liner Combo() using an array of const char*
1366
        // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.
1367
0
        static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview
1368
0
        ImGui::Combo("combo 4 (array)", &item_current_3, items, IM_ARRAYSIZE(items));
1369
1370
        // Simplified one-liner Combo() using an accessor function
1371
0
        static int item_current_4 = 0;
1372
0
        ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items));
1373
1374
0
        ImGui::TreePop();
1375
0
    }
1376
0
}
1377
1378
//-----------------------------------------------------------------------------
1379
// [SECTION] DemoWindowWidgetsDataTypes()
1380
//-----------------------------------------------------------------------------
1381
1382
static void DemoWindowWidgetsDataTypes()
1383
0
{
1384
0
    IMGUI_DEMO_MARKER("Widgets/Data Types");
1385
0
    if (ImGui::TreeNode("Data Types"))
1386
0
    {
1387
        // DragScalar/InputScalar/SliderScalar functions allow various data types
1388
        // - signed/unsigned
1389
        // - 8/16/32/64-bits
1390
        // - integer/float/double
1391
        // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum
1392
        // to pass the type, and passing all arguments by pointer.
1393
        // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each type.
1394
        // In practice, if you frequently use a given type that is not covered by the normal API entry points,
1395
        // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,
1396
        // and then pass their address to the generic function. For example:
1397
        //   bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld")
1398
        //   {
1399
        //      return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);
1400
        //   }
1401
1402
        // Setup limits (as helper variables so we can take their address, as explained above)
1403
        // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.
1404
        #ifndef LLONG_MIN
1405
        ImS64 LLONG_MIN = -9223372036854775807LL - 1;
1406
        ImS64 LLONG_MAX = 9223372036854775807LL;
1407
        ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);
1408
        #endif
1409
0
        const char    s8_zero  = 0,   s8_one  = 1,   s8_fifty  = 50, s8_min  = -128,        s8_max = 127;
1410
0
        const ImU8    u8_zero  = 0,   u8_one  = 1,   u8_fifty  = 50, u8_min  = 0,           u8_max = 255;
1411
0
        const short   s16_zero = 0,   s16_one = 1,   s16_fifty = 50, s16_min = -32768,      s16_max = 32767;
1412
0
        const ImU16   u16_zero = 0,   u16_one = 1,   u16_fifty = 50, u16_min = 0,           u16_max = 65535;
1413
0
        const ImS32   s32_zero = 0,   s32_one = 1,   s32_fifty = 50, s32_min = INT_MIN/2,   s32_max = INT_MAX/2,    s32_hi_a = INT_MAX/2 - 100,    s32_hi_b = INT_MAX/2;
1414
0
        const ImU32   u32_zero = 0,   u32_one = 1,   u32_fifty = 50, u32_min = 0,           u32_max = UINT_MAX/2,   u32_hi_a = UINT_MAX/2 - 100,   u32_hi_b = UINT_MAX/2;
1415
0
        const ImS64   s64_zero = 0,   s64_one = 1,   s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2,  s64_hi_a = LLONG_MAX/2 - 100,  s64_hi_b = LLONG_MAX/2;
1416
0
        const ImU64   u64_zero = 0,   u64_one = 1,   u64_fifty = 50, u64_min = 0,           u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2;
1417
0
        const float   f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;
1418
0
        const double  f64_zero = 0.,  f64_one = 1.,  f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;
1419
1420
        // State
1421
0
        static char   s8_v  = 127;
1422
0
        static ImU8   u8_v  = 255;
1423
0
        static short  s16_v = 32767;
1424
0
        static ImU16  u16_v = 65535;
1425
0
        static ImS32  s32_v = -1;
1426
0
        static ImU32  u32_v = (ImU32)-1;
1427
0
        static ImS64  s64_v = -1;
1428
0
        static ImU64  u64_v = (ImU64)-1;
1429
0
        static float  f32_v = 0.123f;
1430
0
        static double f64_v = 90000.01234567890123456789;
1431
1432
0
        const float drag_speed = 0.2f;
1433
0
        static bool drag_clamp = false;
1434
0
        IMGUI_DEMO_MARKER("Widgets/Data Types/Drags");
1435
0
        ImGui::SeparatorText("Drags");
1436
0
        ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp);
1437
0
        ImGui::SameLine(); HelpMarker(
1438
0
            "As with every widget in dear imgui, we never modify values unless there is a user interaction.\n"
1439
0
            "You can override the clamping limits by using CTRL+Click to input a value.");
1440
0
        ImGui::DragScalar("drag s8",        ImGuiDataType_S8,     &s8_v,  drag_speed, drag_clamp ? &s8_zero  : NULL, drag_clamp ? &s8_fifty  : NULL);
1441
0
        ImGui::DragScalar("drag u8",        ImGuiDataType_U8,     &u8_v,  drag_speed, drag_clamp ? &u8_zero  : NULL, drag_clamp ? &u8_fifty  : NULL, "%u ms");
1442
0
        ImGui::DragScalar("drag s16",       ImGuiDataType_S16,    &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);
1443
0
        ImGui::DragScalar("drag u16",       ImGuiDataType_U16,    &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, "%u ms");
1444
0
        ImGui::DragScalar("drag s32",       ImGuiDataType_S32,    &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);
1445
0
        ImGui::DragScalar("drag s32 hex",   ImGuiDataType_S32,    &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, "0x%08X");
1446
0
        ImGui::DragScalar("drag u32",       ImGuiDataType_U32,    &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms");
1447
0
        ImGui::DragScalar("drag s64",       ImGuiDataType_S64,    &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);
1448
0
        ImGui::DragScalar("drag u64",       ImGuiDataType_U64,    &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);
1449
0
        ImGui::DragScalar("drag float",     ImGuiDataType_Float,  &f32_v, 0.005f,  &f32_zero, &f32_one, "%f");
1450
0
        ImGui::DragScalar("drag float log", ImGuiDataType_Float,  &f32_v, 0.005f,  &f32_zero, &f32_one, "%f", ImGuiSliderFlags_Logarithmic);
1451
0
        ImGui::DragScalar("drag double",    ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL,     "%.10f grams");
1452
0
        ImGui::DragScalar("drag double log",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", ImGuiSliderFlags_Logarithmic);
1453
1454
0
        IMGUI_DEMO_MARKER("Widgets/Data Types/Sliders");
1455
0
        ImGui::SeparatorText("Sliders");
1456
0
        ImGui::SliderScalar("slider s8 full",       ImGuiDataType_S8,     &s8_v,  &s8_min,   &s8_max,   "%d");
1457
0
        ImGui::SliderScalar("slider u8 full",       ImGuiDataType_U8,     &u8_v,  &u8_min,   &u8_max,   "%u");
1458
0
        ImGui::SliderScalar("slider s16 full",      ImGuiDataType_S16,    &s16_v, &s16_min,  &s16_max,  "%d");
1459
0
        ImGui::SliderScalar("slider u16 full",      ImGuiDataType_U16,    &u16_v, &u16_min,  &u16_max,  "%u");
1460
0
        ImGui::SliderScalar("slider s32 low",       ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty,"%d");
1461
0
        ImGui::SliderScalar("slider s32 high",      ImGuiDataType_S32,    &s32_v, &s32_hi_a, &s32_hi_b, "%d");
1462
0
        ImGui::SliderScalar("slider s32 full",      ImGuiDataType_S32,    &s32_v, &s32_min,  &s32_max,  "%d");
1463
0
        ImGui::SliderScalar("slider s32 hex",       ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty, "0x%04X");
1464
0
        ImGui::SliderScalar("slider u32 low",       ImGuiDataType_U32,    &u32_v, &u32_zero, &u32_fifty,"%u");
1465
0
        ImGui::SliderScalar("slider u32 high",      ImGuiDataType_U32,    &u32_v, &u32_hi_a, &u32_hi_b, "%u");
1466
0
        ImGui::SliderScalar("slider u32 full",      ImGuiDataType_U32,    &u32_v, &u32_min,  &u32_max,  "%u");
1467
0
        ImGui::SliderScalar("slider s64 low",       ImGuiDataType_S64,    &s64_v, &s64_zero, &s64_fifty,"%" PRId64);
1468
0
        ImGui::SliderScalar("slider s64 high",      ImGuiDataType_S64,    &s64_v, &s64_hi_a, &s64_hi_b, "%" PRId64);
1469
0
        ImGui::SliderScalar("slider s64 full",      ImGuiDataType_S64,    &s64_v, &s64_min,  &s64_max,  "%" PRId64);
1470
0
        ImGui::SliderScalar("slider u64 low",       ImGuiDataType_U64,    &u64_v, &u64_zero, &u64_fifty,"%" PRIu64 " ms");
1471
0
        ImGui::SliderScalar("slider u64 high",      ImGuiDataType_U64,    &u64_v, &u64_hi_a, &u64_hi_b, "%" PRIu64 " ms");
1472
0
        ImGui::SliderScalar("slider u64 full",      ImGuiDataType_U64,    &u64_v, &u64_min,  &u64_max,  "%" PRIu64 " ms");
1473
0
        ImGui::SliderScalar("slider float low",     ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one);
1474
0
        ImGui::SliderScalar("slider float low log", ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one,  "%.10f", ImGuiSliderFlags_Logarithmic);
1475
0
        ImGui::SliderScalar("slider float high",    ImGuiDataType_Float,  &f32_v, &f32_lo_a, &f32_hi_a, "%e");
1476
0
        ImGui::SliderScalar("slider double low",    ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  "%.10f grams");
1477
0
        ImGui::SliderScalar("slider double low log",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  "%.10f", ImGuiSliderFlags_Logarithmic);
1478
0
        ImGui::SliderScalar("slider double high",   ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams");
1479
1480
0
        ImGui::SeparatorText("Sliders (reverse)");
1481
0
        ImGui::SliderScalar("slider s8 reverse",    ImGuiDataType_S8,   &s8_v,  &s8_max,    &s8_min,   "%d");
1482
0
        ImGui::SliderScalar("slider u8 reverse",    ImGuiDataType_U8,   &u8_v,  &u8_max,    &u8_min,   "%u");
1483
0
        ImGui::SliderScalar("slider s32 reverse",   ImGuiDataType_S32,  &s32_v, &s32_fifty, &s32_zero, "%d");
1484
0
        ImGui::SliderScalar("slider u32 reverse",   ImGuiDataType_U32,  &u32_v, &u32_fifty, &u32_zero, "%u");
1485
0
        ImGui::SliderScalar("slider s64 reverse",   ImGuiDataType_S64,  &s64_v, &s64_fifty, &s64_zero, "%" PRId64);
1486
0
        ImGui::SliderScalar("slider u64 reverse",   ImGuiDataType_U64,  &u64_v, &u64_fifty, &u64_zero, "%" PRIu64 " ms");
1487
1488
0
        IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs");
1489
0
        static bool inputs_step = true;
1490
0
        static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
1491
0
        ImGui::SeparatorText("Inputs");
1492
0
        ImGui::Checkbox("Show step buttons", &inputs_step);
1493
0
        ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
1494
0
        ImGui::CheckboxFlags("ImGuiInputTextFlags_ParseEmptyRefVal", &flags, ImGuiInputTextFlags_ParseEmptyRefVal);
1495
0
        ImGui::CheckboxFlags("ImGuiInputTextFlags_DisplayEmptyRefVal", &flags, ImGuiInputTextFlags_DisplayEmptyRefVal);
1496
0
        ImGui::InputScalar("input s8",      ImGuiDataType_S8,     &s8_v,  inputs_step ? &s8_one  : NULL, NULL, "%d", flags);
1497
0
        ImGui::InputScalar("input u8",      ImGuiDataType_U8,     &u8_v,  inputs_step ? &u8_one  : NULL, NULL, "%u", flags);
1498
0
        ImGui::InputScalar("input s16",     ImGuiDataType_S16,    &s16_v, inputs_step ? &s16_one : NULL, NULL, "%d", flags);
1499
0
        ImGui::InputScalar("input u16",     ImGuiDataType_U16,    &u16_v, inputs_step ? &u16_one : NULL, NULL, "%u", flags);
1500
0
        ImGui::InputScalar("input s32",     ImGuiDataType_S32,    &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d", flags);
1501
0
        ImGui::InputScalar("input s32 hex", ImGuiDataType_S32,    &s32_v, inputs_step ? &s32_one : NULL, NULL, "%04X", flags);
1502
0
        ImGui::InputScalar("input u32",     ImGuiDataType_U32,    &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u", flags);
1503
0
        ImGui::InputScalar("input u32 hex", ImGuiDataType_U32,    &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", flags);
1504
0
        ImGui::InputScalar("input s64",     ImGuiDataType_S64,    &s64_v, inputs_step ? &s64_one : NULL, NULL, NULL, flags);
1505
0
        ImGui::InputScalar("input u64",     ImGuiDataType_U64,    &u64_v, inputs_step ? &u64_one : NULL, NULL, NULL, flags);
1506
0
        ImGui::InputScalar("input float",   ImGuiDataType_Float,  &f32_v, inputs_step ? &f32_one : NULL, NULL, NULL, flags);
1507
0
        ImGui::InputScalar("input double",  ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL, NULL, NULL, flags);
1508
1509
0
        ImGui::TreePop();
1510
0
    }
1511
0
}
1512
1513
//-----------------------------------------------------------------------------
1514
// [SECTION] DemoWindowWidgetsDisableBlocks()
1515
//-----------------------------------------------------------------------------
1516
1517
static void DemoWindowWidgetsDisableBlocks(ImGuiDemoWindowData* demo_data)
1518
0
{
1519
0
    IMGUI_DEMO_MARKER("Widgets/Disable Blocks");
1520
0
    if (ImGui::TreeNode("Disable Blocks"))
1521
0
    {
1522
0
        ImGui::Checkbox("Disable entire section above", &demo_data->DisableSections);
1523
0
        ImGui::SameLine(); HelpMarker("Demonstrate using BeginDisabled()/EndDisabled() across other sections.");
1524
0
        ImGui::TreePop();
1525
0
    }
1526
0
}
1527
1528
//-----------------------------------------------------------------------------
1529
// [SECTION] DemoWindowWidgetsDragAndDrop()
1530
//-----------------------------------------------------------------------------
1531
1532
static void DemoWindowWidgetsDragAndDrop()
1533
0
{
1534
0
    IMGUI_DEMO_MARKER("Widgets/Drag and drop");
1535
0
    if (ImGui::TreeNode("Drag and Drop"))
1536
0
    {
1537
0
        IMGUI_DEMO_MARKER("Widgets/Drag and drop/Standard widgets");
1538
0
        if (ImGui::TreeNode("Drag and drop in standard widgets"))
1539
0
        {
1540
            // ColorEdit widgets automatically act as drag source and drag target.
1541
            // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F
1542
            // to allow your own widgets to use colors in their drag and drop interaction.
1543
            // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.
1544
0
            HelpMarker("You can drag from the color squares.");
1545
0
            static float col1[3] = { 1.0f, 0.0f, 0.2f };
1546
0
            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
1547
0
            ImGui::ColorEdit3("color 1", col1);
1548
0
            ImGui::ColorEdit4("color 2", col2);
1549
0
            ImGui::TreePop();
1550
0
        }
1551
1552
0
        IMGUI_DEMO_MARKER("Widgets/Drag and drop/Copy-swap items");
1553
0
        if (ImGui::TreeNode("Drag and drop to copy/swap items"))
1554
0
        {
1555
0
            enum Mode
1556
0
            {
1557
0
                Mode_Copy,
1558
0
                Mode_Move,
1559
0
                Mode_Swap
1560
0
            };
1561
0
            static int mode = 0;
1562
0
            if (ImGui::RadioButton("Copy", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();
1563
0
            if (ImGui::RadioButton("Move", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();
1564
0
            if (ImGui::RadioButton("Swap", mode == Mode_Swap)) { mode = Mode_Swap; }
1565
0
            static const char* names[9] =
1566
0
            {
1567
0
                "Bobby", "Beatrice", "Betty",
1568
0
                "Brianna", "Barry", "Bernard",
1569
0
                "Bibi", "Blaine", "Bryn"
1570
0
            };
1571
0
            for (int n = 0; n < IM_ARRAYSIZE(names); n++)
1572
0
            {
1573
0
                ImGui::PushID(n);
1574
0
                if ((n % 3) != 0)
1575
0
                    ImGui::SameLine();
1576
0
                ImGui::Button(names[n], ImVec2(60, 60));
1577
1578
                // Our buttons are both drag sources and drag targets here!
1579
0
                if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
1580
0
                {
1581
                    // Set payload to carry the index of our item (could be anything)
1582
0
                    ImGui::SetDragDropPayload("DND_DEMO_CELL", &n, sizeof(int));
1583
1584
                    // Display preview (could be anything, e.g. when dragging an image we could decide to display
1585
                    // the filename and a small preview of the image, etc.)
1586
0
                    if (mode == Mode_Copy) { ImGui::Text("Copy %s", names[n]); }
1587
0
                    if (mode == Mode_Move) { ImGui::Text("Move %s", names[n]); }
1588
0
                    if (mode == Mode_Swap) { ImGui::Text("Swap %s", names[n]); }
1589
0
                    ImGui::EndDragDropSource();
1590
0
                }
1591
0
                if (ImGui::BeginDragDropTarget())
1592
0
                {
1593
0
                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("DND_DEMO_CELL"))
1594
0
                    {
1595
0
                        IM_ASSERT(payload->DataSize == sizeof(int));
1596
0
                        int payload_n = *(const int*)payload->Data;
1597
0
                        if (mode == Mode_Copy)
1598
0
                        {
1599
0
                            names[n] = names[payload_n];
1600
0
                        }
1601
0
                        if (mode == Mode_Move)
1602
0
                        {
1603
0
                            names[n] = names[payload_n];
1604
0
                            names[payload_n] = "";
1605
0
                        }
1606
0
                        if (mode == Mode_Swap)
1607
0
                        {
1608
0
                            const char* tmp = names[n];
1609
0
                            names[n] = names[payload_n];
1610
0
                            names[payload_n] = tmp;
1611
0
                        }
1612
0
                    }
1613
0
                    ImGui::EndDragDropTarget();
1614
0
                }
1615
0
                ImGui::PopID();
1616
0
            }
1617
0
            ImGui::TreePop();
1618
0
        }
1619
1620
0
        IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Drag to reorder items (simple)");
1621
0
        if (ImGui::TreeNode("Drag to reorder items (simple)"))
1622
0
        {
1623
            // FIXME: there is temporary (usually single-frame) ID Conflict during reordering as a same item may be submitting twice.
1624
            // This code was always slightly faulty but in a way which was not easily noticeable.
1625
            // Until we fix this, enable ImGuiItemFlags_AllowDuplicateId to disable detecting the issue.
1626
0
            ImGui::PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true);
1627
1628
            // Simple reordering
1629
0
            HelpMarker(
1630
0
                "We don't use the drag and drop api at all here! "
1631
0
                "Instead we query when the item is held but not hovered, and order items accordingly.");
1632
0
            static const char* item_names[] = { "Item One", "Item Two", "Item Three", "Item Four", "Item Five" };
1633
0
            for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)
1634
0
            {
1635
0
                const char* item = item_names[n];
1636
0
                ImGui::Selectable(item);
1637
1638
0
                if (ImGui::IsItemActive() && !ImGui::IsItemHovered())
1639
0
                {
1640
0
                    int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);
1641
0
                    if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names))
1642
0
                    {
1643
0
                        item_names[n] = item_names[n_next];
1644
0
                        item_names[n_next] = item;
1645
0
                        ImGui::ResetMouseDragDelta();
1646
0
                    }
1647
0
                }
1648
0
            }
1649
1650
0
            ImGui::PopItemFlag();
1651
0
            ImGui::TreePop();
1652
0
        }
1653
1654
0
        IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location");
1655
0
        if (ImGui::TreeNode("Tooltip at target location"))
1656
0
        {
1657
0
            for (int n = 0; n < 2; n++)
1658
0
            {
1659
                // Drop targets
1660
0
                ImGui::Button(n ? "drop here##1" : "drop here##0");
1661
0
                if (ImGui::BeginDragDropTarget())
1662
0
                {
1663
0
                    ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;
1664
0
                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags))
1665
0
                    {
1666
0
                        IM_UNUSED(payload);
1667
0
                        ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);
1668
0
                        ImGui::SetTooltip("Cannot drop here!");
1669
0
                    }
1670
0
                    ImGui::EndDragDropTarget();
1671
0
                }
1672
1673
                // Drop source
1674
0
                static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };
1675
0
                if (n == 0)
1676
0
                    ImGui::ColorButton("drag me", col4);
1677
1678
0
            }
1679
0
            ImGui::TreePop();
1680
0
        }
1681
1682
0
        ImGui::TreePop();
1683
0
    }
1684
0
}
1685
1686
//-----------------------------------------------------------------------------
1687
// [SECTION] DemoWindowWidgetsDragsAndSliders()
1688
//-----------------------------------------------------------------------------
1689
1690
static void DemoWindowWidgetsDragsAndSliders()
1691
0
{
1692
0
    IMGUI_DEMO_MARKER("Widgets/Drag and Slider Flags");
1693
0
    if (ImGui::TreeNode("Drag/Slider Flags"))
1694
0
    {
1695
        // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!
1696
0
        static ImGuiSliderFlags flags = ImGuiSliderFlags_None;
1697
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_AlwaysClamp", &flags, ImGuiSliderFlags_AlwaysClamp);
1698
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_ClampOnInput", &flags, ImGuiSliderFlags_ClampOnInput);
1699
0
        ImGui::SameLine(); HelpMarker("Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.");
1700
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_ClampZeroRange", &flags, ImGuiSliderFlags_ClampZeroRange);
1701
0
        ImGui::SameLine(); HelpMarker("Clamp even if min==max==0.0f. Otherwise DragXXX functions don't clamp.");
1702
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_Logarithmic", &flags, ImGuiSliderFlags_Logarithmic);
1703
0
        ImGui::SameLine(); HelpMarker("Enable logarithmic editing (more precision for small values).");
1704
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_NoRoundToFormat", &flags, ImGuiSliderFlags_NoRoundToFormat);
1705
0
        ImGui::SameLine(); HelpMarker("Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).");
1706
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_NoInput", &flags, ImGuiSliderFlags_NoInput);
1707
0
        ImGui::SameLine(); HelpMarker("Disable CTRL+Click or Enter key allowing to input text directly into the widget.");
1708
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_NoSpeedTweaks", &flags, ImGuiSliderFlags_NoSpeedTweaks);
1709
0
        ImGui::SameLine(); HelpMarker("Disable keyboard modifiers altering tweak speed. Useful if you want to alter tweak speed yourself based on your own logic.");
1710
0
        ImGui::CheckboxFlags("ImGuiSliderFlags_WrapAround", &flags, ImGuiSliderFlags_WrapAround);
1711
0
        ImGui::SameLine(); HelpMarker("Enable wrapping around from max to min and from min to max (only supported by DragXXX() functions)");
1712
1713
        // Drags
1714
0
        static float drag_f = 0.5f;
1715
0
        static int drag_i = 50;
1716
0
        ImGui::Text("Underlying float value: %f", drag_f);
1717
0
        ImGui::DragFloat("DragFloat (0 -> 1)", &drag_f, 0.005f, 0.0f, 1.0f, "%.3f", flags);
1718
0
        ImGui::DragFloat("DragFloat (0 -> +inf)", &drag_f, 0.005f, 0.0f, FLT_MAX, "%.3f", flags);
1719
0
        ImGui::DragFloat("DragFloat (-inf -> 1)", &drag_f, 0.005f, -FLT_MAX, 1.0f, "%.3f", flags);
1720
0
        ImGui::DragFloat("DragFloat (-inf -> +inf)", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, "%.3f", flags);
1721
        //ImGui::DragFloat("DragFloat (0 -> 0)", &drag_f, 0.005f, 0.0f, 0.0f, "%.3f", flags);           // To test ClampZeroRange
1722
        //ImGui::DragFloat("DragFloat (100 -> 100)", &drag_f, 0.005f, 100.0f, 100.0f, "%.3f", flags);
1723
0
        ImGui::DragInt("DragInt (0 -> 100)", &drag_i, 0.5f, 0, 100, "%d", flags);
1724
1725
        // Sliders
1726
0
        static float slider_f = 0.5f;
1727
0
        static int slider_i = 50;
1728
0
        const ImGuiSliderFlags flags_for_sliders = flags & ~ImGuiSliderFlags_WrapAround;
1729
0
        ImGui::Text("Underlying float value: %f", slider_f);
1730
0
        ImGui::SliderFloat("SliderFloat (0 -> 1)", &slider_f, 0.0f, 1.0f, "%.3f", flags_for_sliders);
1731
0
        ImGui::SliderInt("SliderInt (0 -> 100)", &slider_i, 0, 100, "%d", flags_for_sliders);
1732
1733
0
        ImGui::TreePop();
1734
0
    }
1735
0
}
1736
1737
//-----------------------------------------------------------------------------
1738
// [SECTION] DemoWindowWidgetsFonts()
1739
//-----------------------------------------------------------------------------
1740
1741
// Forward declare ShowFontAtlas() which isn't worth putting in public API yet
1742
namespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }
1743
1744
static void DemoWindowWidgetsFonts()
1745
0
{
1746
0
    IMGUI_DEMO_MARKER("Widgets/Fonts");
1747
0
    if (ImGui::TreeNode("Fonts"))
1748
0
    {
1749
0
        ImFontAtlas* atlas = ImGui::GetIO().Fonts;
1750
0
        ImGui::ShowFontAtlas(atlas);
1751
        // FIXME-NEWATLAS: Provide a demo to add/create a procedural font?
1752
0
        ImGui::TreePop();
1753
0
    }
1754
0
}
1755
1756
//-----------------------------------------------------------------------------
1757
// [SECTION] DemoWindowWidgetsImages()
1758
//-----------------------------------------------------------------------------
1759
1760
static void DemoWindowWidgetsImages()
1761
0
{
1762
0
    IMGUI_DEMO_MARKER("Widgets/Images");
1763
0
    if (ImGui::TreeNode("Images"))
1764
0
    {
1765
0
        ImGuiIO& io = ImGui::GetIO();
1766
0
        ImGui::TextWrapped(
1767
0
            "Below we are displaying the font texture (which is the only texture we have access to in this demo). "
1768
0
            "Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. "
1769
0
            "Hover the texture for a zoomed view!");
1770
1771
        // Below we are displaying the font texture because it is the only texture we have access to inside the demo!
1772
        // Read description about ImTextureID/ImTextureRef and FAQ for details about texture identifiers.
1773
        // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top
1774
        // of their respective source file to specify what they are using as texture identifier, for example:
1775
        // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer.
1776
        // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.
1777
        // So with the DirectX11 backend, you call ImGui::Image() with a 'ID3D11ShaderResourceView*' cast to ImTextureID.
1778
        // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers
1779
        //   to ImGui::Image(), and gather width/height through your own functions, etc.
1780
        // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,
1781
        //   it will help you debug issues if you are confused about it.
1782
        // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().
1783
        // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md
1784
        // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1785
1786
        // Grab the current texture identifier used by the font atlas.
1787
0
        ImTextureRef my_tex_id = io.Fonts->TexRef;
1788
1789
        // Regular user code should never have to care about TexData-> fields, but since we want to display the entire texture here, we pull Width/Height from it.
1790
0
        float my_tex_w = (float)io.Fonts->TexData->Width;
1791
0
        float my_tex_h = (float)io.Fonts->TexData->Height;
1792
1793
0
        {
1794
0
            ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h);
1795
0
            ImVec2 pos = ImGui::GetCursorScreenPos();
1796
0
            ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left
1797
0
            ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right
1798
0
            ImGui::PushStyleVar(ImGuiStyleVar_ImageBorderSize, IM_MAX(1.0f, ImGui::GetStyle().ImageBorderSize));
1799
0
            ImGui::ImageWithBg(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
1800
0
            if (ImGui::BeginItemTooltip())
1801
0
            {
1802
0
                float region_sz = 32.0f;
1803
0
                float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;
1804
0
                float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;
1805
0
                float zoom = 4.0f;
1806
0
                if (region_x < 0.0f) { region_x = 0.0f; }
1807
0
                else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }
1808
0
                if (region_y < 0.0f) { region_y = 0.0f; }
1809
0
                else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }
1810
0
                ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y);
1811
0
                ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz);
1812
0
                ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);
1813
0
                ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);
1814
0
                ImGui::ImageWithBg(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
1815
0
                ImGui::EndTooltip();
1816
0
            }
1817
0
            ImGui::PopStyleVar();
1818
0
        }
1819
1820
0
        IMGUI_DEMO_MARKER("Widgets/Images/Textured buttons");
1821
0
        ImGui::TextWrapped("And now some textured buttons..");
1822
0
        static int pressed_count = 0;
1823
0
        for (int i = 0; i < 8; i++)
1824
0
        {
1825
            // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.
1826
            // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.
1827
            // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1828
0
            ImGui::PushID(i);
1829
0
            if (i > 0)
1830
0
                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f));
1831
0
            ImVec2 size = ImVec2(32.0f, 32.0f);                         // Size of the image we want to make visible
1832
0
            ImVec2 uv0 = ImVec2(0.0f, 0.0f);                            // UV coordinates for lower-left
1833
0
            ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h);    // UV coordinates for (32,32) in our texture
1834
0
            ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);             // Black background
1835
0
            ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);           // No tint
1836
0
            if (ImGui::ImageButton("", my_tex_id, size, uv0, uv1, bg_col, tint_col))
1837
0
                pressed_count += 1;
1838
0
            if (i > 0)
1839
0
                ImGui::PopStyleVar();
1840
0
            ImGui::PopID();
1841
0
            ImGui::SameLine();
1842
0
        }
1843
0
        ImGui::NewLine();
1844
0
        ImGui::Text("Pressed %d times.", pressed_count);
1845
0
        ImGui::TreePop();
1846
0
    }
1847
0
}
1848
1849
//-----------------------------------------------------------------------------
1850
// [SECTION] DemoWindowWidgetsListBoxes()
1851
//-----------------------------------------------------------------------------
1852
1853
static void DemoWindowWidgetsListBoxes()
1854
0
{
1855
0
    IMGUI_DEMO_MARKER("Widgets/List Boxes");
1856
0
    if (ImGui::TreeNode("List Boxes"))
1857
0
    {
1858
        // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild()
1859
        // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
1860
        // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild()
1861
        // to always be called (inconsistent with BeginListBox()/EndListBox()).
1862
1863
        // Using the generic BeginListBox() API, you have full control over how to display the combo contents.
1864
        // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively
1865
        // stored in the object itself, etc.)
1866
0
        const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" };
1867
0
        static int item_selected_idx = 0; // Here we store our selected data as an index.
1868
1869
0
        static bool item_highlight = false;
1870
0
        int item_highlighted_idx = -1; // Here we store our highlighted data as an index.
1871
0
        ImGui::Checkbox("Highlight hovered item in second listbox", &item_highlight);
1872
1873
0
        if (ImGui::BeginListBox("listbox 1"))
1874
0
        {
1875
0
            for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1876
0
            {
1877
0
                const bool is_selected = (item_selected_idx == n);
1878
0
                if (ImGui::Selectable(items[n], is_selected))
1879
0
                    item_selected_idx = n;
1880
1881
0
                if (item_highlight && ImGui::IsItemHovered())
1882
0
                    item_highlighted_idx = n;
1883
1884
                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1885
0
                if (is_selected)
1886
0
                    ImGui::SetItemDefaultFocus();
1887
0
            }
1888
0
            ImGui::EndListBox();
1889
0
        }
1890
0
        ImGui::SameLine(); HelpMarker("Here we are sharing selection state between both boxes.");
1891
1892
        // Custom size: use all width, 5 items tall
1893
0
        ImGui::Text("Full-width:");
1894
0
        if (ImGui::BeginListBox("##listbox 2", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))
1895
0
        {
1896
0
            for (int n = 0; n < IM_ARRAYSIZE(items); n++)
1897
0
            {
1898
0
                bool is_selected = (item_selected_idx == n);
1899
0
                ImGuiSelectableFlags flags = (item_highlighted_idx == n) ? ImGuiSelectableFlags_Highlight : 0;
1900
0
                if (ImGui::Selectable(items[n], is_selected, flags))
1901
0
                    item_selected_idx = n;
1902
1903
                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1904
0
                if (is_selected)
1905
0
                    ImGui::SetItemDefaultFocus();
1906
0
            }
1907
0
            ImGui::EndListBox();
1908
0
        }
1909
1910
0
        ImGui::TreePop();
1911
0
    }
1912
0
}
1913
1914
//-----------------------------------------------------------------------------
1915
// [SECTION] DemoWindowWidgetsMultiComponents()
1916
//-----------------------------------------------------------------------------
1917
1918
static void DemoWindowWidgetsMultiComponents()
1919
0
{
1920
0
    IMGUI_DEMO_MARKER("Widgets/Multi-component Widgets");
1921
0
    if (ImGui::TreeNode("Multi-component Widgets"))
1922
0
    {
1923
0
        static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };
1924
0
        static int vec4i[4] = { 1, 5, 100, 255 };
1925
1926
0
        ImGui::SeparatorText("2-wide");
1927
0
        ImGui::InputFloat2("input float2", vec4f);
1928
0
        ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f);
1929
0
        ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f);
1930
0
        ImGui::InputInt2("input int2", vec4i);
1931
0
        ImGui::DragInt2("drag int2", vec4i, 1, 0, 255);
1932
0
        ImGui::SliderInt2("slider int2", vec4i, 0, 255);
1933
1934
0
        ImGui::SeparatorText("3-wide");
1935
0
        ImGui::InputFloat3("input float3", vec4f);
1936
0
        ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f);
1937
0
        ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f);
1938
0
        ImGui::InputInt3("input int3", vec4i);
1939
0
        ImGui::DragInt3("drag int3", vec4i, 1, 0, 255);
1940
0
        ImGui::SliderInt3("slider int3", vec4i, 0, 255);
1941
1942
0
        ImGui::SeparatorText("4-wide");
1943
0
        ImGui::InputFloat4("input float4", vec4f);
1944
0
        ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f);
1945
0
        ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f);
1946
0
        ImGui::InputInt4("input int4", vec4i);
1947
0
        ImGui::DragInt4("drag int4", vec4i, 1, 0, 255);
1948
0
        ImGui::SliderInt4("slider int4", vec4i, 0, 255);
1949
1950
0
        ImGui::SeparatorText("Ranges");
1951
0
        static float begin = 10, end = 90;
1952
0
        static int begin_i = 100, end_i = 1000;
1953
0
        ImGui::DragFloatRange2("range float", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%", ImGuiSliderFlags_AlwaysClamp);
1954
0
        ImGui::DragIntRange2("range int", &begin_i, &end_i, 5, 0, 1000, "Min: %d units", "Max: %d units");
1955
0
        ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units");
1956
1957
0
        ImGui::TreePop();
1958
0
    }
1959
0
}
1960
1961
//-----------------------------------------------------------------------------
1962
// [SECTION] DemoWindowWidgetsPlotting()
1963
//-----------------------------------------------------------------------------
1964
1965
static void DemoWindowWidgetsPlotting()
1966
0
{
1967
    // Plot/Graph widgets are not very good.
1968
// Consider using a third-party library such as ImPlot: https://github.com/epezent/implot
1969
// (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)
1970
0
    IMGUI_DEMO_MARKER("Widgets/Plotting");
1971
0
    if (ImGui::TreeNode("Plotting"))
1972
0
    {
1973
0
        ImGui::Text("Need better plotting and graphing? Consider using ImPlot:");
1974
0
        ImGui::TextLinkOpenURL("https://github.com/epezent/implot");
1975
0
        ImGui::Separator();
1976
1977
0
        static bool animate = true;
1978
0
        ImGui::Checkbox("Animate", &animate);
1979
1980
        // Plot as lines and plot as histogram
1981
0
        static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
1982
0
        ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr));
1983
0
        ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));
1984
        //ImGui::SameLine(); HelpMarker("Consider using ImPlot instead!");
1985
1986
        // Fill an array of contiguous float values to plot
1987
        // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float
1988
        // and the sizeof() of your structure in the "stride" parameter.
1989
0
        static float values[90] = {};
1990
0
        static int values_offset = 0;
1991
0
        static double refresh_time = 0.0;
1992
0
        if (!animate || refresh_time == 0.0)
1993
0
            refresh_time = ImGui::GetTime();
1994
0
        while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo
1995
0
        {
1996
0
            static float phase = 0.0f;
1997
0
            values[values_offset] = cosf(phase);
1998
0
            values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
1999
0
            phase += 0.10f * values_offset;
2000
0
            refresh_time += 1.0f / 60.0f;
2001
0
        }
2002
2003
        // Plots can display overlay texts
2004
        // (in this example, we will display an average value)
2005
0
        {
2006
0
            float average = 0.0f;
2007
0
            for (int n = 0; n < IM_ARRAYSIZE(values); n++)
2008
0
                average += values[n];
2009
0
            average /= (float)IM_ARRAYSIZE(values);
2010
0
            char overlay[32];
2011
0
            sprintf(overlay, "avg %f", average);
2012
0
            ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));
2013
0
        }
2014
2015
        // Use functions to generate output
2016
        // FIXME: This is actually VERY awkward because current plot API only pass in indices.
2017
        // We probably want an API passing floats and user provide sample rate/count.
2018
0
        struct Funcs
2019
0
        {
2020
0
            static float Sin(void*, int i) { return sinf(i * 0.1f); }
2021
0
            static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }
2022
0
        };
2023
0
        static int func_type = 0, display_count = 70;
2024
0
        ImGui::SeparatorText("Functions");
2025
0
        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
2026
0
        ImGui::Combo("func", &func_type, "Sin\0Saw\0");
2027
0
        ImGui::SameLine();
2028
0
        ImGui::SliderInt("Sample count", &display_count, 1, 400);
2029
0
        float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;
2030
0
        ImGui::PlotLines("Lines##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
2031
0
        ImGui::PlotHistogram("Histogram##2", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));
2032
2033
0
        ImGui::TreePop();
2034
0
    }
2035
0
}
2036
2037
//-----------------------------------------------------------------------------
2038
// [SECTION] DemoWindowWidgetsProgressBars()
2039
//-----------------------------------------------------------------------------
2040
2041
static void DemoWindowWidgetsProgressBars()
2042
0
{
2043
0
    IMGUI_DEMO_MARKER("Widgets/Progress Bars");
2044
0
    if (ImGui::TreeNode("Progress Bars"))
2045
0
    {
2046
        // Animate a simple progress bar
2047
0
        static float progress = 0.0f, progress_dir = 1.0f;
2048
0
        progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;
2049
0
        if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; }
2050
0
        if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; }
2051
2052
        // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,
2053
        // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.
2054
0
        ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));
2055
0
        ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
2056
0
        ImGui::Text("Progress Bar");
2057
2058
0
        float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);
2059
0
        char buf[32];
2060
0
        sprintf(buf, "%d/%d", (int)(progress_saturated * 1753), 1753);
2061
0
        ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);
2062
2063
        // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value.
2064
        // Adjust the factor if you want to adjust the animation speed.
2065
0
        ImGui::ProgressBar(-1.0f * (float)ImGui::GetTime(), ImVec2(0.0f, 0.0f), "Searching..");
2066
0
        ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
2067
0
        ImGui::Text("Indeterminate");
2068
2069
0
        ImGui::TreePop();
2070
0
    }
2071
0
}
2072
2073
//-----------------------------------------------------------------------------
2074
// [SECTION] DemoWindowWidgetsQueryingStatuses()
2075
//-----------------------------------------------------------------------------
2076
2077
static void DemoWindowWidgetsQueryingStatuses()
2078
0
{
2079
0
    IMGUI_DEMO_MARKER("Widgets/Querying Item Status (Edited,Active,Hovered etc.)");
2080
0
    if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)"))
2081
0
    {
2082
        // Select an item type
2083
0
        const char* item_names[] =
2084
0
        {
2085
0
            "Text", "Button", "Button (w/ repeat)", "Checkbox", "SliderFloat", "InputText", "InputTextMultiline", "InputFloat",
2086
0
            "InputFloat3", "ColorEdit4", "Selectable", "MenuItem", "TreeNode", "TreeNode (w/ double-click)", "Combo", "ListBox"
2087
0
        };
2088
0
        static int item_type = 4;
2089
0
        static bool item_disabled = false;
2090
0
        ImGui::Combo("Item Type", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));
2091
0
        ImGui::SameLine();
2092
0
        HelpMarker("Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered().");
2093
0
        ImGui::Checkbox("Item Disabled", &item_disabled);
2094
2095
        // Submit selected items so we can query their status in the code following it.
2096
0
        bool ret = false;
2097
0
        static bool b = false;
2098
0
        static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };
2099
0
        static char str[16] = {};
2100
0
        if (item_disabled)
2101
0
            ImGui::BeginDisabled(true);
2102
0
        if (item_type == 0) { ImGui::Text("ITEM: Text"); }                                              // Testing text items with no identifier/interaction
2103
0
        if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); }                                    // Testing button
2104
0
        if (item_type == 2) { ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat, true); ret = ImGui::Button("ITEM: Button"); ImGui::PopItemFlag(); } // Testing button (with repeater)
2105
0
        if (item_type == 3) { ret = ImGui::Checkbox("ITEM: Checkbox", &b); }                            // Testing checkbox
2106
0
        if (item_type == 4) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); }   // Testing basic item
2107
0
        if (item_type == 5) { ret = ImGui::InputText("ITEM: InputText", &str[0], IM_ARRAYSIZE(str)); }  // Testing input text (which handles tabbing)
2108
0
        if (item_type == 6) { ret = ImGui::InputTextMultiline("ITEM: InputTextMultiline", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window)
2109
0
        if (item_type == 7) { ret = ImGui::InputFloat("ITEM: InputFloat", col4f, 1.0f); }               // Testing +/- buttons on scalar input
2110
0
        if (item_type == 8) { ret = ImGui::InputFloat3("ITEM: InputFloat3", col4f); }                   // Testing multi-component items (IsItemXXX flags are reported merged)
2111
0
        if (item_type == 9) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); }                     // Testing multi-component items (IsItemXXX flags are reported merged)
2112
0
        if (item_type == 10) { ret = ImGui::Selectable("ITEM: Selectable"); }                            // Testing selectable item
2113
0
        if (item_type == 11) { ret = ImGui::MenuItem("ITEM: MenuItem"); }                                // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)
2114
0
        if (item_type == 12) { ret = ImGui::TreeNode("ITEM: TreeNode"); if (ret) ImGui::TreePop(); }     // Testing tree node
2115
0
        if (item_type == 13) { ret = ImGui::TreeNodeEx("ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.
2116
0
        if (item_type == 14) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::Combo("ITEM: Combo", &current, items, IM_ARRAYSIZE(items)); }
2117
0
        if (item_type == 15) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
2118
2119
0
        bool hovered_delay_none = ImGui::IsItemHovered();
2120
0
        bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary);
2121
0
        bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort);
2122
0
        bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal);
2123
0
        bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary
2124
2125
        // Display the values of IsItemHovered() and other common item state functions.
2126
        // Note that the ImGuiHoveredFlags_XXX flags can be combined.
2127
        // Because BulletText is an item itself and that would affect the output of IsItemXXX functions,
2128
        // we query every state in a single call to avoid storing them and to simplify the code.
2129
0
        ImGui::BulletText(
2130
0
            "Return value = %d\n"
2131
0
            "IsItemFocused() = %d\n"
2132
0
            "IsItemHovered() = %d\n"
2133
0
            "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
2134
0
            "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2135
0
            "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n"
2136
0
            "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n"
2137
0
            "IsItemHovered(_AllowWhenDisabled) = %d\n"
2138
0
            "IsItemHovered(_RectOnly) = %d\n"
2139
0
            "IsItemActive() = %d\n"
2140
0
            "IsItemEdited() = %d\n"
2141
0
            "IsItemActivated() = %d\n"
2142
0
            "IsItemDeactivated() = %d\n"
2143
0
            "IsItemDeactivatedAfterEdit() = %d\n"
2144
0
            "IsItemVisible() = %d\n"
2145
0
            "IsItemClicked() = %d\n"
2146
0
            "IsItemToggledOpen() = %d\n"
2147
0
            "GetItemRectMin() = (%.1f, %.1f)\n"
2148
0
            "GetItemRectMax() = (%.1f, %.1f)\n"
2149
0
            "GetItemRectSize() = (%.1f, %.1f)",
2150
0
            ret,
2151
0
            ImGui::IsItemFocused(),
2152
0
            ImGui::IsItemHovered(),
2153
0
            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2154
0
            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2155
0
            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem),
2156
0
            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow),
2157
0
            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled),
2158
0
            ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),
2159
0
            ImGui::IsItemActive(),
2160
0
            ImGui::IsItemEdited(),
2161
0
            ImGui::IsItemActivated(),
2162
0
            ImGui::IsItemDeactivated(),
2163
0
            ImGui::IsItemDeactivatedAfterEdit(),
2164
0
            ImGui::IsItemVisible(),
2165
0
            ImGui::IsItemClicked(),
2166
0
            ImGui::IsItemToggledOpen(),
2167
0
            ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,
2168
0
            ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,
2169
0
            ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y
2170
0
        );
2171
0
        ImGui::BulletText(
2172
0
            "with Hovering Delay or Stationary test:\n"
2173
0
            "IsItemHovered() = %d\n"
2174
0
            "IsItemHovered(_Stationary) = %d\n"
2175
0
            "IsItemHovered(_DelayShort) = %d\n"
2176
0
            "IsItemHovered(_DelayNormal) = %d\n"
2177
0
            "IsItemHovered(_Tooltip) = %d",
2178
0
            hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);
2179
2180
0
        if (item_disabled)
2181
0
            ImGui::EndDisabled();
2182
2183
0
        char buf[1] = "";
2184
0
        ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly);
2185
0
        ImGui::SameLine();
2186
0
        HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");
2187
2188
0
        ImGui::TreePop();
2189
0
    }
2190
2191
0
    IMGUI_DEMO_MARKER("Widgets/Querying Window Status (Focused,Hovered etc.)");
2192
0
    if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)"))
2193
0
    {
2194
0
        static bool embed_all_inside_a_child_window = false;
2195
0
        ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window);
2196
0
        if (embed_all_inside_a_child_window)
2197
0
            ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Borders);
2198
2199
        // Testing IsWindowFocused() function with its various flags.
2200
0
        ImGui::BulletText(
2201
0
            "IsWindowFocused() = %d\n"
2202
0
            "IsWindowFocused(_ChildWindows) = %d\n"
2203
0
            "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
2204
0
            "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
2205
0
            "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2206
0
            "IsWindowFocused(_RootWindow) = %d\n"
2207
0
            "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
2208
0
            "IsWindowFocused(_AnyWindow) = %d\n",
2209
0
            ImGui::IsWindowFocused(),
2210
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),
2211
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
2212
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
2213
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2214
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),
2215
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
2216
0
            ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));
2217
2218
        // Testing IsWindowHovered() function with its various flags.
2219
0
        ImGui::BulletText(
2220
0
            "IsWindowHovered() = %d\n"
2221
0
            "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
2222
0
            "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
2223
0
            "IsWindowHovered(_ChildWindows) = %d\n"
2224
0
            "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
2225
0
            "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
2226
0
            "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
2227
0
            "IsWindowHovered(_RootWindow) = %d\n"
2228
0
            "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
2229
0
            "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
2230
0
            "IsWindowHovered(_AnyWindow) = %d\n"
2231
0
            "IsWindowHovered(_Stationary) = %d\n",
2232
0
            ImGui::IsWindowHovered(),
2233
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2234
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
2235
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),
2236
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
2237
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
2238
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2239
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
2240
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
2241
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
2242
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),
2243
0
            ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));
2244
2245
0
        ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Borders);
2246
0
        ImGui::Text("This is another child window for testing the _ChildWindows flag.");
2247
0
        ImGui::EndChild();
2248
0
        if (embed_all_inside_a_child_window)
2249
0
            ImGui::EndChild();
2250
2251
        // Calling IsItemHovered() after begin returns the hovered status of the title bar.
2252
        // This is useful in particular if you want to create a context menu associated to the title bar of a window.
2253
0
        static bool test_window = false;
2254
0
        ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window);
2255
0
        if (test_window)
2256
0
        {
2257
0
            ImGui::Begin("Title bar Hovered/Active tests", &test_window);
2258
0
            if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()
2259
0
            {
2260
0
                if (ImGui::MenuItem("Close")) { test_window = false; }
2261
0
                ImGui::EndPopup();
2262
0
            }
2263
0
            ImGui::Text(
2264
0
                "IsItemHovered() after begin = %d (== is title bar hovered)\n"
2265
0
                "IsItemActive() after begin = %d (== is window being clicked/moved)\n",
2266
0
                ImGui::IsItemHovered(), ImGui::IsItemActive());
2267
0
            ImGui::End();
2268
0
        }
2269
2270
0
        ImGui::TreePop();
2271
0
    }
2272
0
}
2273
2274
//-----------------------------------------------------------------------------
2275
// [SECTION] DemoWindowWidgetsSelectables()
2276
//-----------------------------------------------------------------------------
2277
2278
static void DemoWindowWidgetsSelectables()
2279
0
{
2280
0
    IMGUI_DEMO_MARKER("Widgets/Selectables");
2281
    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
2282
0
    if (ImGui::TreeNode("Selectables"))
2283
0
    {
2284
        // Selectable() has 2 overloads:
2285
        // - The one taking "bool selected" as a read-only selection information.
2286
        //   When Selectable() has been clicked it returns true and you can alter selection state accordingly.
2287
        // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases)
2288
        // The earlier is more flexible, as in real application your selection may be stored in many different ways
2289
        // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).
2290
0
        IMGUI_DEMO_MARKER("Widgets/Selectables/Basic");
2291
0
        if (ImGui::TreeNode("Basic"))
2292
0
        {
2293
0
            static bool selection[5] = { false, true, false, false };
2294
0
            ImGui::Selectable("1. I am selectable", &selection[0]);
2295
0
            ImGui::Selectable("2. I am selectable", &selection[1]);
2296
0
            ImGui::Selectable("3. I am selectable", &selection[2]);
2297
0
            if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick))
2298
0
                if (ImGui::IsMouseDoubleClicked(0))
2299
0
                    selection[3] = !selection[3];
2300
0
            ImGui::TreePop();
2301
0
        }
2302
2303
0
        IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line");
2304
0
        if (ImGui::TreeNode("Rendering more items on the same line"))
2305
0
        {
2306
            // (1) Using SetNextItemAllowOverlap()
2307
            // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically.
2308
0
            static bool selected[3] = { false, false, false };
2309
0
            ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1");
2310
0
            ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2");
2311
0
            ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3");
2312
0
            ImGui::TreePop();
2313
0
        }
2314
2315
0
        IMGUI_DEMO_MARKER("Widgets/Selectables/In Tables");
2316
0
        if (ImGui::TreeNode("In Tables"))
2317
0
        {
2318
0
            static bool selected[10] = {};
2319
2320
0
            if (ImGui::BeginTable("split1", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2321
0
            {
2322
0
                for (int i = 0; i < 10; i++)
2323
0
                {
2324
0
                    char label[32];
2325
0
                    sprintf(label, "Item %d", i);
2326
0
                    ImGui::TableNextColumn();
2327
0
                    ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap
2328
0
                }
2329
0
                ImGui::EndTable();
2330
0
            }
2331
0
            ImGui::Spacing();
2332
0
            if (ImGui::BeginTable("split2", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))
2333
0
            {
2334
0
                for (int i = 0; i < 10; i++)
2335
0
                {
2336
0
                    char label[32];
2337
0
                    sprintf(label, "Item %d", i);
2338
0
                    ImGui::TableNextRow();
2339
0
                    ImGui::TableNextColumn();
2340
0
                    ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns);
2341
0
                    ImGui::TableNextColumn();
2342
0
                    ImGui::Text("Some other contents");
2343
0
                    ImGui::TableNextColumn();
2344
0
                    ImGui::Text("123456");
2345
0
                }
2346
0
                ImGui::EndTable();
2347
0
            }
2348
0
            ImGui::TreePop();
2349
0
        }
2350
2351
0
        IMGUI_DEMO_MARKER("Widgets/Selectables/Grid");
2352
0
        if (ImGui::TreeNode("Grid"))
2353
0
        {
2354
0
            static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
2355
2356
            // Add in a bit of silly fun...
2357
0
            const float time = (float)ImGui::GetTime();
2358
0
            const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected...
2359
0
            if (winning_state)
2360
0
                ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f)));
2361
2362
0
            for (int y = 0; y < 4; y++)
2363
0
                for (int x = 0; x < 4; x++)
2364
0
                {
2365
0
                    if (x > 0)
2366
0
                        ImGui::SameLine();
2367
0
                    ImGui::PushID(y * 4 + x);
2368
0
                    if (ImGui::Selectable("Sailor", selected[y][x] != 0, 0, ImVec2(50, 50)))
2369
0
                    {
2370
                        // Toggle clicked cell + toggle neighbors
2371
0
                        selected[y][x] ^= 1;
2372
0
                        if (x > 0) { selected[y][x - 1] ^= 1; }
2373
0
                        if (x < 3) { selected[y][x + 1] ^= 1; }
2374
0
                        if (y > 0) { selected[y - 1][x] ^= 1; }
2375
0
                        if (y < 3) { selected[y + 1][x] ^= 1; }
2376
0
                    }
2377
0
                    ImGui::PopID();
2378
0
                }
2379
2380
0
            if (winning_state)
2381
0
                ImGui::PopStyleVar();
2382
0
            ImGui::TreePop();
2383
0
        }
2384
0
        IMGUI_DEMO_MARKER("Widgets/Selectables/Alignment");
2385
0
        if (ImGui::TreeNode("Alignment"))
2386
0
        {
2387
0
            HelpMarker(
2388
0
                "By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item "
2389
0
                "basis using PushStyleVar(). You'll probably want to always keep your default situation to "
2390
0
                "left-align otherwise it becomes difficult to layout multiple items on a same line");
2391
0
            static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };
2392
0
            for (int y = 0; y < 3; y++)
2393
0
            {
2394
0
                for (int x = 0; x < 3; x++)
2395
0
                {
2396
0
                    ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);
2397
0
                    char name[32];
2398
0
                    sprintf(name, "(%.1f,%.1f)", alignment.x, alignment.y);
2399
0
                    if (x > 0) ImGui::SameLine();
2400
0
                    ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);
2401
0
                    ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80));
2402
0
                    ImGui::PopStyleVar();
2403
0
                }
2404
0
            }
2405
0
            ImGui::TreePop();
2406
0
        }
2407
0
        ImGui::TreePop();
2408
0
    }
2409
0
}
2410
2411
//-----------------------------------------------------------------------------
2412
// [SECTION] DemoWindowWidgetsSelectionAndMultiSelect()
2413
//-----------------------------------------------------------------------------
2414
// Multi-selection demos
2415
// Also read: https://github.com/ocornut/imgui/wiki/Multi-Select
2416
//-----------------------------------------------------------------------------
2417
2418
static const char* ExampleNames[] =
2419
{
2420
    "Artichoke", "Arugula", "Asparagus", "Avocado", "Bamboo Shoots", "Bean Sprouts", "Beans", "Beet", "Belgian Endive", "Bell Pepper",
2421
    "Bitter Gourd", "Bok Choy", "Broccoli", "Brussels Sprouts", "Burdock Root", "Cabbage", "Calabash", "Capers", "Carrot", "Cassava",
2422
    "Cauliflower", "Celery", "Celery Root", "Celcuce", "Chayote", "Chinese Broccoli", "Corn", "Cucumber"
2423
};
2424
2425
// Extra functions to add deletion support to ImGuiSelectionBasicStorage
2426
struct ExampleSelectionWithDeletion : ImGuiSelectionBasicStorage
2427
{
2428
    // Find which item should be Focused after deletion.
2429
    // Call _before_ item submission. Return an index in the before-deletion item list, your item loop should call SetKeyboardFocusHere() on it.
2430
    // The subsequent ApplyDeletionPostLoop() code will use it to apply Selection.
2431
    // - We cannot provide this logic in core Dear ImGui because we don't have access to selection data.
2432
    // - We don't actually manipulate the ImVector<> here, only in ApplyDeletionPostLoop(), but using similar API for consistency and flexibility.
2433
    // - Important: Deletion only works if the underlying ImGuiID for your items are stable: aka not depend on their index, but on e.g. item id/ptr.
2434
    // FIXME-MULTISELECT: Doesn't take account of the possibility focus target will be moved during deletion. Need refocus or scroll offset.
2435
    int ApplyDeletionPreLoop(ImGuiMultiSelectIO* ms_io, int items_count)
2436
0
    {
2437
0
        if (Size == 0)
2438
0
            return -1;
2439
2440
        // If focused item is not selected...
2441
0
        const int focused_idx = (int)ms_io->NavIdItem;  // Index of currently focused item
2442
0
        if (ms_io->NavIdSelected == false)  // This is merely a shortcut, == Contains(adapter->IndexToStorage(items, focused_idx))
2443
0
        {
2444
0
            ms_io->RangeSrcReset = true;    // Request to recover RangeSrc from NavId next frame. Would be ok to reset even when NavIdSelected==true, but it would take an extra frame to recover RangeSrc when deleting a selected item.
2445
0
            return focused_idx;             // Request to focus same item after deletion.
2446
0
        }
2447
2448
        // If focused item is selected: land on first unselected item after focused item.
2449
0
        for (int idx = focused_idx + 1; idx < items_count; idx++)
2450
0
            if (!Contains(GetStorageIdFromIndex(idx)))
2451
0
                return idx;
2452
2453
        // If focused item is selected: otherwise return last unselected item before focused item.
2454
0
        for (int idx = IM_MIN(focused_idx, items_count) - 1; idx >= 0; idx--)
2455
0
            if (!Contains(GetStorageIdFromIndex(idx)))
2456
0
                return idx;
2457
2458
0
        return -1;
2459
0
    }
2460
2461
    // Rewrite item list (delete items) + update selection.
2462
    // - Call after EndMultiSelect()
2463
    // - We cannot provide this logic in core Dear ImGui because we don't have access to your items, nor to selection data.
2464
    template<typename ITEM_TYPE>
2465
    void ApplyDeletionPostLoop(ImGuiMultiSelectIO* ms_io, ImVector<ITEM_TYPE>& items, int item_curr_idx_to_select)
2466
0
    {
2467
        // Rewrite item list (delete items) + convert old selection index (before deletion) to new selection index (after selection).
2468
        // If NavId was not part of selection, we will stay on same item.
2469
0
        ImVector<ITEM_TYPE> new_items;
2470
0
        new_items.reserve(items.Size - Size);
2471
0
        int item_next_idx_to_select = -1;
2472
0
        for (int idx = 0; idx < items.Size; idx++)
2473
0
        {
2474
0
            if (!Contains(GetStorageIdFromIndex(idx)))
2475
0
                new_items.push_back(items[idx]);
2476
0
            if (item_curr_idx_to_select == idx)
2477
0
                item_next_idx_to_select = new_items.Size - 1;
2478
0
        }
2479
0
        items.swap(new_items);
2480
2481
        // Update selection
2482
0
        Clear();
2483
0
        if (item_next_idx_to_select != -1 && ms_io->NavIdSelected)
2484
0
            SetItemSelected(GetStorageIdFromIndex(item_next_idx_to_select), true);
2485
0
    }
Unexecuted instantiation: _ZN28ExampleSelectionWithDeletion21ApplyDeletionPostLoopIjEEvP18ImGuiMultiSelectIOR8ImVectorIT_Ei
Unexecuted instantiation: _ZN28ExampleSelectionWithDeletion21ApplyDeletionPostLoopIiEEvP18ImGuiMultiSelectIOR8ImVectorIT_Ei
Unexecuted instantiation: _ZN28ExampleSelectionWithDeletion21ApplyDeletionPostLoopI12ExampleAssetEEvP18ImGuiMultiSelectIOR8ImVectorIT_Ei
2486
};
2487
2488
// Example: Implement dual list box storage and interface
2489
struct ExampleDualListBox
2490
{
2491
    ImVector<ImGuiID>           Items[2];               // ID is index into ExampleName[]
2492
    ImGuiSelectionBasicStorage  Selections[2];          // Store ExampleItemId into selection
2493
    bool                        OptKeepSorted = true;
2494
2495
    void MoveAll(int src, int dst)
2496
0
    {
2497
0
        IM_ASSERT((src == 0 && dst == 1) || (src == 1 && dst == 0));
2498
0
        for (ImGuiID item_id : Items[src])
2499
0
            Items[dst].push_back(item_id);
2500
0
        Items[src].clear();
2501
0
        SortItems(dst);
2502
0
        Selections[src].Swap(Selections[dst]);
2503
0
        Selections[src].Clear();
2504
0
    }
2505
    void MoveSelected(int src, int dst)
2506
0
    {
2507
0
        for (int src_n = 0; src_n < Items[src].Size; src_n++)
2508
0
        {
2509
0
            ImGuiID item_id = Items[src][src_n];
2510
0
            if (!Selections[src].Contains(item_id))
2511
0
                continue;
2512
0
            Items[src].erase(&Items[src][src_n]); // FIXME-OPT: Could be implemented more optimally (rebuild src items and swap)
2513
0
            Items[dst].push_back(item_id);
2514
0
            src_n--;
2515
0
        }
2516
0
        if (OptKeepSorted)
2517
0
            SortItems(dst);
2518
0
        Selections[src].Swap(Selections[dst]);
2519
0
        Selections[src].Clear();
2520
0
    }
2521
    void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, int side)
2522
0
    {
2523
        // In this example we store item id in selection (instead of item index)
2524
0
        Selections[side].UserData = Items[side].Data;
2525
0
        Selections[side].AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImGuiID* items = (ImGuiID*)self->UserData; return items[idx]; };
2526
0
        Selections[side].ApplyRequests(ms_io);
2527
0
    }
2528
    static int IMGUI_CDECL CompareItemsByValue(const void* lhs, const void* rhs)
2529
0
    {
2530
0
        const int* a = (const int*)lhs;
2531
0
        const int* b = (const int*)rhs;
2532
0
        return (*a - *b);
2533
0
    }
2534
    void SortItems(int n)
2535
0
    {
2536
0
        qsort(Items[n].Data, (size_t)Items[n].Size, sizeof(Items[n][0]), CompareItemsByValue);
2537
0
    }
2538
    void Show()
2539
0
    {
2540
        //if (ImGui::Checkbox("Sorted", &OptKeepSorted) && OptKeepSorted) { SortItems(0); SortItems(1); }
2541
0
        if (ImGui::BeginTable("split", 3, ImGuiTableFlags_None))
2542
0
        {
2543
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);    // Left side
2544
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);      // Buttons
2545
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);    // Right side
2546
0
            ImGui::TableNextRow();
2547
2548
0
            int request_move_selected = -1;
2549
0
            int request_move_all = -1;
2550
0
            float child_height_0 = 0.0f;
2551
0
            for (int side = 0; side < 2; side++)
2552
0
            {
2553
                // FIXME-MULTISELECT: Dual List Box: Add context menus
2554
                // FIXME-NAV: Using ImGuiWindowFlags_NavFlattened exhibit many issues.
2555
0
                ImVector<ImGuiID>& items = Items[side];
2556
0
                ImGuiSelectionBasicStorage& selection = Selections[side];
2557
2558
0
                ImGui::TableSetColumnIndex((side == 0) ? 0 : 2);
2559
0
                ImGui::Text("%s (%d)", (side == 0) ? "Available" : "Basket", items.Size);
2560
2561
                // Submit scrolling range to avoid glitches on moving/deletion
2562
0
                const float items_height = ImGui::GetTextLineHeightWithSpacing();
2563
0
                ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2564
2565
0
                bool child_visible;
2566
0
                if (side == 0)
2567
0
                {
2568
                    // Left child is resizable
2569
0
                    ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetFrameHeightWithSpacing() * 4), ImVec2(FLT_MAX, FLT_MAX));
2570
0
                    child_visible = ImGui::BeginChild("0", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY);
2571
0
                    child_height_0 = ImGui::GetWindowSize().y;
2572
0
                }
2573
0
                else
2574
0
                {
2575
                    // Right child use same height as left one
2576
0
                    child_visible = ImGui::BeginChild("1", ImVec2(-FLT_MIN, child_height_0), ImGuiChildFlags_FrameStyle);
2577
0
                }
2578
0
                if (child_visible)
2579
0
                {
2580
0
                    ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_None;
2581
0
                    ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
2582
0
                    ApplySelectionRequests(ms_io, side);
2583
2584
0
                    for (int item_n = 0; item_n < items.Size; item_n++)
2585
0
                    {
2586
0
                        ImGuiID item_id = items[item_n];
2587
0
                        bool item_is_selected = selection.Contains(item_id);
2588
0
                        ImGui::SetNextItemSelectionUserData(item_n);
2589
0
                        ImGui::Selectable(ExampleNames[item_id], item_is_selected, ImGuiSelectableFlags_AllowDoubleClick);
2590
0
                        if (ImGui::IsItemFocused())
2591
0
                        {
2592
                            // FIXME-MULTISELECT: Dual List Box: Transfer focus
2593
0
                            if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter))
2594
0
                                request_move_selected = side;
2595
0
                            if (ImGui::IsMouseDoubleClicked(0)) // FIXME-MULTISELECT: Double-click on multi-selection?
2596
0
                                request_move_selected = side;
2597
0
                        }
2598
0
                    }
2599
2600
0
                    ms_io = ImGui::EndMultiSelect();
2601
0
                    ApplySelectionRequests(ms_io, side);
2602
0
                }
2603
0
                ImGui::EndChild();
2604
0
            }
2605
2606
            // Buttons columns
2607
0
            ImGui::TableSetColumnIndex(1);
2608
0
            ImGui::NewLine();
2609
            //ImVec2 button_sz = { ImGui::CalcTextSize(">>").x + ImGui::GetStyle().FramePadding.x * 2.0f, ImGui::GetFrameHeight() + padding.y * 2.0f };
2610
0
            ImVec2 button_sz = { ImGui::GetFrameHeight(), ImGui::GetFrameHeight() };
2611
2612
            // (Using BeginDisabled()/EndDisabled() works but feels distracting given how it is currently visualized)
2613
0
            if (ImGui::Button(">>", button_sz))
2614
0
                request_move_all = 0;
2615
0
            if (ImGui::Button(">", button_sz))
2616
0
                request_move_selected = 0;
2617
0
            if (ImGui::Button("<", button_sz))
2618
0
                request_move_selected = 1;
2619
0
            if (ImGui::Button("<<", button_sz))
2620
0
                request_move_all = 1;
2621
2622
            // Process requests
2623
0
            if (request_move_all != -1)
2624
0
                MoveAll(request_move_all, request_move_all ^ 1);
2625
0
            if (request_move_selected != -1)
2626
0
                MoveSelected(request_move_selected, request_move_selected ^ 1);
2627
2628
            // FIXME-MULTISELECT: Support action from outside
2629
            /*
2630
            if (OptKeepSorted == false)
2631
            {
2632
                ImGui::NewLine();
2633
                if (ImGui::ArrowButton("MoveUp", ImGuiDir_Up)) {}
2634
                if (ImGui::ArrowButton("MoveDown", ImGuiDir_Down)) {}
2635
            }
2636
            */
2637
2638
0
            ImGui::EndTable();
2639
0
        }
2640
0
    }
2641
};
2642
2643
static void DemoWindowWidgetsSelectionAndMultiSelect(ImGuiDemoWindowData* demo_data)
2644
0
{
2645
0
    IMGUI_DEMO_MARKER("Widgets/Selection State & Multi-Select");
2646
0
    if (ImGui::TreeNode("Selection State & Multi-Select"))
2647
0
    {
2648
0
        HelpMarker("Selections can be built using Selectable(), TreeNode() or other widgets. Selection state is owned by application code/data.");
2649
2650
        // Without any fancy API: manage single-selection yourself.
2651
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Single-Select");
2652
0
        if (ImGui::TreeNode("Single-Select"))
2653
0
        {
2654
0
            static int selected = -1;
2655
0
            for (int n = 0; n < 5; n++)
2656
0
            {
2657
0
                char buf[32];
2658
0
                sprintf(buf, "Object %d", n);
2659
0
                if (ImGui::Selectable(buf, selected == n))
2660
0
                    selected = n;
2661
0
            }
2662
0
            ImGui::TreePop();
2663
0
        }
2664
2665
        // Demonstrate implementation a most-basic form of multi-selection manually
2666
        // This doesn't support the SHIFT modifier which requires BeginMultiSelect()!
2667
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (manual/simplified, without BeginMultiSelect)");
2668
0
        if (ImGui::TreeNode("Multi-Select (manual/simplified, without BeginMultiSelect)"))
2669
0
        {
2670
0
            HelpMarker("Hold CTRL and click to select multiple items.");
2671
0
            static bool selection[5] = { false, false, false, false, false };
2672
0
            for (int n = 0; n < 5; n++)
2673
0
            {
2674
0
                char buf[32];
2675
0
                sprintf(buf, "Object %d", n);
2676
0
                if (ImGui::Selectable(buf, selection[n]))
2677
0
                {
2678
0
                    if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held
2679
0
                        memset(selection, 0, sizeof(selection));
2680
0
                    selection[n] ^= 1; // Toggle current item
2681
0
                }
2682
0
            }
2683
0
            ImGui::TreePop();
2684
0
        }
2685
2686
        // Demonstrate handling proper multi-selection using the BeginMultiSelect/EndMultiSelect API.
2687
        // SHIFT+Click w/ CTRL and other standard features are supported.
2688
        // We use the ImGuiSelectionBasicStorage helper which you may freely reimplement.
2689
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select");
2690
0
        if (ImGui::TreeNode("Multi-Select"))
2691
0
        {
2692
0
            ImGui::Text("Supported features:");
2693
0
            ImGui::BulletText("Keyboard navigation (arrows, page up/down, home/end, space).");
2694
0
            ImGui::BulletText("Ctrl modifier to preserve and toggle selection.");
2695
0
            ImGui::BulletText("Shift modifier for range selection.");
2696
0
            ImGui::BulletText("CTRL+A to select all.");
2697
0
            ImGui::BulletText("Escape to clear selection.");
2698
0
            ImGui::BulletText("Click and drag to box-select.");
2699
0
            ImGui::Text("Tip: Use 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.");
2700
2701
            // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2702
0
            const int ITEMS_COUNT = 50;
2703
0
            static ImGuiSelectionBasicStorage selection;
2704
0
            ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2705
2706
            // The BeginChild() has no purpose for selection logic, other that offering a scrolling region.
2707
0
            if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2708
0
            {
2709
0
                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2710
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2711
0
                selection.ApplyRequests(ms_io);
2712
2713
0
                for (int n = 0; n < ITEMS_COUNT; n++)
2714
0
                {
2715
0
                    char label[64];
2716
0
                    sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2717
0
                    bool item_is_selected = selection.Contains((ImGuiID)n);
2718
0
                    ImGui::SetNextItemSelectionUserData(n);
2719
0
                    ImGui::Selectable(label, item_is_selected);
2720
0
                }
2721
2722
0
                ms_io = ImGui::EndMultiSelect();
2723
0
                selection.ApplyRequests(ms_io);
2724
0
            }
2725
0
            ImGui::EndChild();
2726
0
            ImGui::TreePop();
2727
0
        }
2728
2729
        // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2730
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with clipper)");
2731
0
        if (ImGui::TreeNode("Multi-Select (with clipper)"))
2732
0
        {
2733
            // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
2734
0
            static ImGuiSelectionBasicStorage selection;
2735
2736
0
            ImGui::Text("Added features:");
2737
0
            ImGui::BulletText("Using ImGuiListClipper.");
2738
2739
0
            const int ITEMS_COUNT = 10000;
2740
0
            ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2741
0
            if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2742
0
            {
2743
0
                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2744
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2745
0
                selection.ApplyRequests(ms_io);
2746
2747
0
                ImGuiListClipper clipper;
2748
0
                clipper.Begin(ITEMS_COUNT);
2749
0
                if (ms_io->RangeSrcItem != -1)
2750
0
                    clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2751
0
                while (clipper.Step())
2752
0
                {
2753
0
                    for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2754
0
                    {
2755
0
                        char label[64];
2756
0
                        sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2757
0
                        bool item_is_selected = selection.Contains((ImGuiID)n);
2758
0
                        ImGui::SetNextItemSelectionUserData(n);
2759
0
                        ImGui::Selectable(label, item_is_selected);
2760
0
                    }
2761
0
                }
2762
2763
0
                ms_io = ImGui::EndMultiSelect();
2764
0
                selection.ApplyRequests(ms_io);
2765
0
            }
2766
0
            ImGui::EndChild();
2767
0
            ImGui::TreePop();
2768
0
        }
2769
2770
        // Demonstrate dynamic item list + deletion support using the BeginMultiSelect/EndMultiSelect API.
2771
        // In order to support Deletion without any glitches you need to:
2772
        // - (1) If items are submitted in their own scrolling area, submit contents size SetNextWindowContentSize() ahead of time to prevent one-frame readjustment of scrolling.
2773
        // - (2) Items needs to have persistent ID Stack identifier = ID needs to not depends on their index. PushID(index) = KO. PushID(item_id) = OK. This is in order to focus items reliably after a selection.
2774
        // - (3) BeginXXXX process
2775
        // - (4) Focus process
2776
        // - (5) EndXXXX process
2777
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (with deletion)");
2778
0
        if (ImGui::TreeNode("Multi-Select (with deletion)"))
2779
0
        {
2780
            // Storing items data separately from selection data.
2781
            // (you may decide to store selection data inside your item (aka intrusive storage) if you don't need multiple views over same items)
2782
            // Use a custom selection.Adapter: store item identifier in Selection (instead of index)
2783
0
            static ImVector<ImGuiID> items;
2784
0
            static ExampleSelectionWithDeletion selection;
2785
0
            selection.UserData = (void*)&items;
2786
0
            selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self, int idx) { ImVector<ImGuiID>* p_items = (ImVector<ImGuiID>*)self->UserData; return (*p_items)[idx]; }; // Index -> ID
2787
2788
0
            ImGui::Text("Added features:");
2789
0
            ImGui::BulletText("Dynamic list with Delete key support.");
2790
0
            ImGui::Text("Selection size: %d/%d", selection.Size, items.Size);
2791
2792
            // Initialize default list with 50 items + button to add/remove items.
2793
0
            static ImGuiID items_next_id = 0;
2794
0
            if (items_next_id == 0)
2795
0
                for (ImGuiID n = 0; n < 50; n++)
2796
0
                    items.push_back(items_next_id++);
2797
0
            if (ImGui::SmallButton("Add 20 items"))     { for (int n = 0; n < 20; n++) { items.push_back(items_next_id++); } }
2798
0
            ImGui::SameLine();
2799
0
            if (ImGui::SmallButton("Remove 20 items"))  { for (int n = IM_MIN(20, items.Size); n > 0; n--) { selection.SetItemSelected(items.back(), false); items.pop_back(); } }
2800
2801
            // (1) Extra to support deletion: Submit scrolling range to avoid glitches on deletion
2802
0
            const float items_height = ImGui::GetTextLineHeightWithSpacing();
2803
0
            ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
2804
2805
0
            if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
2806
0
            {
2807
0
                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2808
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
2809
0
                selection.ApplyRequests(ms_io);
2810
2811
0
                const bool want_delete = ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0);
2812
0
                const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
2813
2814
0
                for (int n = 0; n < items.Size; n++)
2815
0
                {
2816
0
                    const ImGuiID item_id = items[n];
2817
0
                    char label[64];
2818
0
                    sprintf(label, "Object %05u: %s", item_id, ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)]);
2819
2820
0
                    bool item_is_selected = selection.Contains(item_id);
2821
0
                    ImGui::SetNextItemSelectionUserData(n);
2822
0
                    ImGui::Selectable(label, item_is_selected);
2823
0
                    if (item_curr_idx_to_focus == n)
2824
0
                        ImGui::SetKeyboardFocusHere(-1);
2825
0
                }
2826
2827
                // Apply multi-select requests
2828
0
                ms_io = ImGui::EndMultiSelect();
2829
0
                selection.ApplyRequests(ms_io);
2830
0
                if (want_delete)
2831
0
                    selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
2832
0
            }
2833
0
            ImGui::EndChild();
2834
0
            ImGui::TreePop();
2835
0
        }
2836
2837
        // Implement a Dual List Box (#6648)
2838
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (dual list box)");
2839
0
        if (ImGui::TreeNode("Multi-Select (dual list box)"))
2840
0
        {
2841
            // Init default state
2842
0
            static ExampleDualListBox dlb;
2843
0
            if (dlb.Items[0].Size == 0 && dlb.Items[1].Size == 0)
2844
0
                for (int item_id = 0; item_id < IM_ARRAYSIZE(ExampleNames); item_id++)
2845
0
                    dlb.Items[0].push_back((ImGuiID)item_id);
2846
2847
            // Show
2848
0
            dlb.Show();
2849
2850
0
            ImGui::TreePop();
2851
0
        }
2852
2853
        // Demonstrate using the clipper with BeginMultiSelect()/EndMultiSelect()
2854
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (in a table)");
2855
0
        if (ImGui::TreeNode("Multi-Select (in a table)"))
2856
0
        {
2857
0
            static ImGuiSelectionBasicStorage selection;
2858
2859
0
            const int ITEMS_COUNT = 10000;
2860
0
            ImGui::Text("Selection: %d/%d", selection.Size, ITEMS_COUNT);
2861
0
            if (ImGui::BeginTable("##Basket", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter))
2862
0
            {
2863
0
                ImGui::TableSetupColumn("Object");
2864
0
                ImGui::TableSetupColumn("Action");
2865
0
                ImGui::TableSetupScrollFreeze(0, 1);
2866
0
                ImGui::TableHeadersRow();
2867
2868
0
                ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
2869
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, ITEMS_COUNT);
2870
0
                selection.ApplyRequests(ms_io);
2871
2872
0
                ImGuiListClipper clipper;
2873
0
                clipper.Begin(ITEMS_COUNT);
2874
0
                if (ms_io->RangeSrcItem != -1)
2875
0
                    clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
2876
0
                while (clipper.Step())
2877
0
                {
2878
0
                    for (int n = clipper.DisplayStart; n < clipper.DisplayEnd; n++)
2879
0
                    {
2880
0
                        ImGui::TableNextRow();
2881
0
                        ImGui::TableNextColumn();
2882
0
                        char label[64];
2883
0
                        sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2884
0
                        bool item_is_selected = selection.Contains((ImGuiID)n);
2885
0
                        ImGui::SetNextItemSelectionUserData(n);
2886
0
                        ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
2887
0
                        ImGui::TableNextColumn();
2888
0
                        ImGui::SmallButton("hello");
2889
0
                    }
2890
0
                }
2891
2892
0
                ms_io = ImGui::EndMultiSelect();
2893
0
                selection.ApplyRequests(ms_io);
2894
0
                ImGui::EndTable();
2895
0
            }
2896
0
            ImGui::TreePop();
2897
0
        }
2898
2899
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (checkboxes)");
2900
0
        if (ImGui::TreeNode("Multi-Select (checkboxes)"))
2901
0
        {
2902
0
            ImGui::Text("In a list of checkboxes (not selectable):");
2903
0
            ImGui::BulletText("Using _NoAutoSelect + _NoAutoClear flags.");
2904
0
            ImGui::BulletText("Shift+Click to check multiple boxes.");
2905
0
            ImGui::BulletText("Shift+Keyboard to copy current value to other boxes.");
2906
2907
            // If you have an array of checkboxes, you may want to use NoAutoSelect + NoAutoClear and the ImGuiSelectionExternalStorage helper.
2908
0
            static bool items[20] = {};
2909
0
            static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_NoAutoSelect | ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_ClearOnEscape;
2910
0
            ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
2911
0
            ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
2912
0
            ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d); // Cannot use ImGuiMultiSelectFlags_BoxSelect1d as checkboxes are varying width.
2913
2914
0
            if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
2915
0
            {
2916
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, -1, IM_ARRAYSIZE(items));
2917
0
                ImGuiSelectionExternalStorage storage_wrapper;
2918
0
                storage_wrapper.UserData = (void*)items;
2919
0
                storage_wrapper.AdapterSetItemSelected = [](ImGuiSelectionExternalStorage* self, int n, bool selected) { bool* array = (bool*)self->UserData; array[n] = selected; };
2920
0
                storage_wrapper.ApplyRequests(ms_io);
2921
0
                for (int n = 0; n < 20; n++)
2922
0
                {
2923
0
                    char label[32];
2924
0
                    sprintf(label, "Item %d", n);
2925
0
                    ImGui::SetNextItemSelectionUserData(n);
2926
0
                    ImGui::Checkbox(label, &items[n]);
2927
0
                }
2928
0
                ms_io = ImGui::EndMultiSelect();
2929
0
                storage_wrapper.ApplyRequests(ms_io);
2930
0
            }
2931
0
            ImGui::EndChild();
2932
2933
0
            ImGui::TreePop();
2934
0
        }
2935
2936
        // Demonstrate individual selection scopes in same window
2937
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (multiple scopes)");
2938
0
        if (ImGui::TreeNode("Multi-Select (multiple scopes)"))
2939
0
        {
2940
            // Use default select: Pass index to SetNextItemSelectionUserData(), store index in Selection
2941
0
            const int SCOPES_COUNT = 3;
2942
0
            const int ITEMS_COUNT = 8; // Per scope
2943
0
            static ImGuiSelectionBasicStorage selections_data[SCOPES_COUNT];
2944
2945
            // Use ImGuiMultiSelectFlags_ScopeRect to not affect other selections in same window.
2946
0
            static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ScopeRect | ImGuiMultiSelectFlags_ClearOnEscape;// | ImGuiMultiSelectFlags_ClearOnClickVoid;
2947
0
            if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
2948
0
                flags &= ~ImGuiMultiSelectFlags_ScopeRect;
2949
0
            if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
2950
0
                flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
2951
0
            ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);
2952
0
            ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
2953
2954
0
            for (int selection_scope_n = 0; selection_scope_n < SCOPES_COUNT; selection_scope_n++)
2955
0
            {
2956
0
                ImGui::PushID(selection_scope_n);
2957
0
                ImGuiSelectionBasicStorage* selection = &selections_data[selection_scope_n];
2958
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection->Size, ITEMS_COUNT);
2959
0
                selection->ApplyRequests(ms_io);
2960
2961
0
                ImGui::SeparatorText("Selection scope");
2962
0
                ImGui::Text("Selection size: %d/%d", selection->Size, ITEMS_COUNT);
2963
2964
0
                for (int n = 0; n < ITEMS_COUNT; n++)
2965
0
                {
2966
0
                    char label[64];
2967
0
                    sprintf(label, "Object %05d: %s", n, ExampleNames[n % IM_ARRAYSIZE(ExampleNames)]);
2968
0
                    bool item_is_selected = selection->Contains((ImGuiID)n);
2969
0
                    ImGui::SetNextItemSelectionUserData(n);
2970
0
                    ImGui::Selectable(label, item_is_selected);
2971
0
                }
2972
2973
                // Apply multi-select requests
2974
0
                ms_io = ImGui::EndMultiSelect();
2975
0
                selection->ApplyRequests(ms_io);
2976
0
                ImGui::PopID();
2977
0
            }
2978
0
            ImGui::TreePop();
2979
0
        }
2980
2981
        // See ShowExampleAppAssetsBrowser()
2982
0
        if (ImGui::TreeNode("Multi-Select (tiled assets browser)"))
2983
0
        {
2984
0
            ImGui::Checkbox("Assets Browser", &demo_data->ShowAppAssetsBrowser);
2985
0
            ImGui::Text("(also access from 'Examples->Assets Browser' in menu)");
2986
0
            ImGui::TreePop();
2987
0
        }
2988
2989
        // Demonstrate supporting multiple-selection in a tree.
2990
        // - We don't use linear indices for selection user data, but our ExampleTreeNode* pointer directly!
2991
        //   This showcase how SetNextItemSelectionUserData() never assume indices!
2992
        // - The difficulty here is to "interpolate" from RangeSrcItem to RangeDstItem in the SetAll/SetRange request.
2993
        //   We want this interpolation to match what the user sees: in visible order, skipping closed nodes.
2994
        //   This is implemented by our TreeGetNextNodeInVisibleOrder() user-space helper.
2995
        // - Important: In a real codebase aiming to implement full-featured selectable tree with custom filtering, you
2996
        //   are more likely to build an array mapping sequential indices to visible tree nodes, since your
2997
        //   filtering/search + clipping process will benefit from it. Having this will make this interpolation much easier.
2998
        // - Consider this a prototype: we are working toward simplifying some of it.
2999
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (trees)");
3000
0
        if (ImGui::TreeNode("Multi-Select (trees)"))
3001
0
        {
3002
0
            HelpMarker(
3003
0
                "This is rather advanced and experimental. If you are getting started with multi-select, "
3004
0
                "please don't start by looking at how to use it for a tree!\n\n"
3005
0
                "Future versions will try to simplify and formalize some of this.");
3006
3007
0
            struct ExampleTreeFuncs
3008
0
            {
3009
0
                static void DrawNode(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection)
3010
0
                {
3011
0
                    ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3012
0
                    tree_node_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent; // Enable pressing left to jump to parent
3013
0
                    if (node->Childs.Size == 0)
3014
0
                        tree_node_flags |= ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_Leaf;
3015
0
                    if (selection->Contains((ImGuiID)node->UID))
3016
0
                        tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3017
3018
                    // Using SetNextItemStorageID() to specify storage id, so we can easily peek into
3019
                    // the storage holding open/close stage, using our TreeNodeGetOpen/TreeNodeSetOpen() functions.
3020
0
                    ImGui::SetNextItemSelectionUserData((ImGuiSelectionUserData)(intptr_t)node);
3021
0
                    ImGui::SetNextItemStorageID((ImGuiID)node->UID);
3022
0
                    if (ImGui::TreeNodeEx(node->Name, tree_node_flags))
3023
0
                    {
3024
0
                        for (ExampleTreeNode* child : node->Childs)
3025
0
                            DrawNode(child, selection);
3026
0
                        ImGui::TreePop();
3027
0
                    }
3028
0
                    else if (ImGui::IsItemToggledOpen())
3029
0
                    {
3030
0
                        TreeCloseAndUnselectChildNodes(node, selection);
3031
0
                    }
3032
0
                }
3033
3034
0
                static bool TreeNodeGetOpen(ExampleTreeNode* node)
3035
0
                {
3036
0
                    return ImGui::GetStateStorage()->GetBool((ImGuiID)node->UID);
3037
0
                }
3038
3039
0
                static void TreeNodeSetOpen(ExampleTreeNode* node, bool open)
3040
0
                {
3041
0
                    ImGui::GetStateStorage()->SetBool((ImGuiID)node->UID, open);
3042
0
                }
3043
3044
                // When closing a node: 1) close and unselect all child nodes, 2) select parent if any child was selected.
3045
                // FIXME: This is currently handled by user logic but I'm hoping to eventually provide tree node
3046
                // features to do this automatically, e.g. a ImGuiTreeNodeFlags_AutoCloseChildNodes etc.
3047
0
                static int TreeCloseAndUnselectChildNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, int depth = 0)
3048
0
                {
3049
                    // Recursive close (the test for depth == 0 is because we call this on a node that was just closed!)
3050
0
                    int unselected_count = selection->Contains((ImGuiID)node->UID) ? 1 : 0;
3051
0
                    if (depth == 0 || TreeNodeGetOpen(node))
3052
0
                    {
3053
0
                        for (ExampleTreeNode* child : node->Childs)
3054
0
                            unselected_count += TreeCloseAndUnselectChildNodes(child, selection, depth + 1);
3055
0
                        TreeNodeSetOpen(node, false);
3056
0
                    }
3057
3058
                    // Select root node if any of its child was selected, otherwise unselect
3059
0
                    selection->SetItemSelected((ImGuiID)node->UID, (depth == 0 && unselected_count > 0));
3060
0
                    return unselected_count;
3061
0
                }
3062
3063
                // Apply multi-selection requests
3064
0
                static void ApplySelectionRequests(ImGuiMultiSelectIO* ms_io, ExampleTreeNode* tree, ImGuiSelectionBasicStorage* selection)
3065
0
                {
3066
0
                    for (ImGuiSelectionRequest& req : ms_io->Requests)
3067
0
                    {
3068
0
                        if (req.Type == ImGuiSelectionRequestType_SetAll)
3069
0
                        {
3070
0
                            if (req.Selected)
3071
0
                                TreeSetAllInOpenNodes(tree, selection, req.Selected);
3072
0
                            else
3073
0
                                selection->Clear();
3074
0
                        }
3075
0
                        else if (req.Type == ImGuiSelectionRequestType_SetRange)
3076
0
                        {
3077
0
                            ExampleTreeNode* first_node = (ExampleTreeNode*)(intptr_t)req.RangeFirstItem;
3078
0
                            ExampleTreeNode* last_node = (ExampleTreeNode*)(intptr_t)req.RangeLastItem;
3079
0
                            for (ExampleTreeNode* node = first_node; node != NULL; node = TreeGetNextNodeInVisibleOrder(node, last_node))
3080
0
                                selection->SetItemSelected((ImGuiID)node->UID, req.Selected);
3081
0
                        }
3082
0
                    }
3083
0
                }
3084
3085
0
                static void TreeSetAllInOpenNodes(ExampleTreeNode* node, ImGuiSelectionBasicStorage* selection, bool selected)
3086
0
                {
3087
0
                    if (node->Parent != NULL) // Root node isn't visible nor selectable in our scheme
3088
0
                        selection->SetItemSelected((ImGuiID)node->UID, selected);
3089
0
                    if (node->Parent == NULL || TreeNodeGetOpen(node))
3090
0
                        for (ExampleTreeNode* child : node->Childs)
3091
0
                            TreeSetAllInOpenNodes(child, selection, selected);
3092
0
                }
3093
3094
                // Interpolate in *user-visible order* AND only *over opened nodes*.
3095
                // If you have a sequential mapping tables (e.g. generated after a filter/search pass) this would be simpler.
3096
                // Here the tricks are that:
3097
                // - we store/maintain ExampleTreeNode::IndexInParent which allows implementing a linear iterator easily, without searches, without recursion.
3098
                //   this could be replaced by a search in parent, aka 'int index_in_parent = curr_node->Parent->Childs.find_index(curr_node)'
3099
                //   which would only be called when crossing from child to a parent, aka not too much.
3100
                // - we call SetNextItemStorageID() before our TreeNode() calls with an ID which doesn't relate to UI stack,
3101
                //   making it easier to call TreeNodeGetOpen()/TreeNodeSetOpen() from any location.
3102
0
                static ExampleTreeNode* TreeGetNextNodeInVisibleOrder(ExampleTreeNode* curr_node, ExampleTreeNode* last_node)
3103
0
                {
3104
                    // Reached last node
3105
0
                    if (curr_node == last_node)
3106
0
                        return NULL;
3107
3108
                    // Recurse into childs. Query storage to tell if the node is open.
3109
0
                    if (curr_node->Childs.Size > 0 && TreeNodeGetOpen(curr_node))
3110
0
                        return curr_node->Childs[0];
3111
3112
                    // Next sibling, then into our own parent
3113
0
                    while (curr_node->Parent != NULL)
3114
0
                    {
3115
0
                        if (curr_node->IndexInParent + 1 < curr_node->Parent->Childs.Size)
3116
0
                            return curr_node->Parent->Childs[curr_node->IndexInParent + 1];
3117
0
                        curr_node = curr_node->Parent;
3118
0
                    }
3119
0
                    return NULL;
3120
0
                }
3121
3122
0
            }; // ExampleTreeFuncs
3123
3124
0
            static ImGuiSelectionBasicStorage selection;
3125
0
            if (demo_data->DemoTree == NULL)
3126
0
                demo_data->DemoTree = ExampleTree_CreateDemoTree(); // Create tree once
3127
0
            ImGui::Text("Selection size: %d", selection.Size);
3128
3129
0
            if (ImGui::BeginChild("##Tree", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3130
0
            {
3131
0
                ExampleTreeNode* tree = demo_data->DemoTree;
3132
0
                ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect2d;
3133
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, selection.Size, -1);
3134
0
                ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);
3135
0
                for (ExampleTreeNode* node : tree->Childs)
3136
0
                    ExampleTreeFuncs::DrawNode(node, &selection);
3137
0
                ms_io = ImGui::EndMultiSelect();
3138
0
                ExampleTreeFuncs::ApplySelectionRequests(ms_io, tree, &selection);
3139
0
            }
3140
0
            ImGui::EndChild();
3141
3142
0
            ImGui::TreePop();
3143
0
        }
3144
3145
        // Advanced demonstration of BeginMultiSelect()
3146
        // - Showcase clipping.
3147
        // - Showcase deletion.
3148
        // - Showcase basic drag and drop.
3149
        // - Showcase TreeNode variant (note that tree node don't expand in the demo: supporting expanding tree nodes + clipping a separate thing).
3150
        // - Showcase using inside a table.
3151
0
        IMGUI_DEMO_MARKER("Widgets/Selection State/Multi-Select (advanced)");
3152
        //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
3153
0
        if (ImGui::TreeNode("Multi-Select (advanced)"))
3154
0
        {
3155
            // Options
3156
0
            enum WidgetType { WidgetType_Selectable, WidgetType_TreeNode };
3157
0
            static bool use_clipper = true;
3158
0
            static bool use_deletion = true;
3159
0
            static bool use_drag_drop = true;
3160
0
            static bool show_in_table = false;
3161
0
            static bool show_color_button = true;
3162
0
            static ImGuiMultiSelectFlags flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_BoxSelect1d;
3163
0
            static WidgetType widget_type = WidgetType_Selectable;
3164
3165
0
            if (ImGui::TreeNode("Options"))
3166
0
            {
3167
0
                if (ImGui::RadioButton("Selectables", widget_type == WidgetType_Selectable)) { widget_type = WidgetType_Selectable; }
3168
0
                ImGui::SameLine();
3169
0
                if (ImGui::RadioButton("Tree nodes", widget_type == WidgetType_TreeNode)) { widget_type = WidgetType_TreeNode; }
3170
0
                ImGui::SameLine();
3171
0
                HelpMarker("TreeNode() is technically supported but... using this correctly is more complicated (you need some sort of linear/random access to your tree, which is suited to advanced trees setups already implementing filters and clipper. We will work toward simplifying and demoing this.\n\nFor now the tree demo is actually a little bit meaningless because it is an empty tree with only root nodes.");
3172
0
                ImGui::Checkbox("Enable clipper", &use_clipper);
3173
0
                ImGui::Checkbox("Enable deletion", &use_deletion);
3174
0
                ImGui::Checkbox("Enable drag & drop", &use_drag_drop);
3175
0
                ImGui::Checkbox("Show in a table", &show_in_table);
3176
0
                ImGui::Checkbox("Show color button", &show_color_button);
3177
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SingleSelect", &flags, ImGuiMultiSelectFlags_SingleSelect);
3178
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoSelectAll", &flags, ImGuiMultiSelectFlags_NoSelectAll);
3179
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoRangeSelect", &flags, ImGuiMultiSelectFlags_NoRangeSelect);
3180
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoSelect", &flags, ImGuiMultiSelectFlags_NoAutoSelect);
3181
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClear", &flags, ImGuiMultiSelectFlags_NoAutoClear);
3182
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_NoAutoClearOnReselect", &flags, ImGuiMultiSelectFlags_NoAutoClearOnReselect);
3183
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect1d", &flags, ImGuiMultiSelectFlags_BoxSelect1d);
3184
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelect2d", &flags, ImGuiMultiSelectFlags_BoxSelect2d);
3185
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_BoxSelectNoScroll", &flags, ImGuiMultiSelectFlags_BoxSelectNoScroll);
3186
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnEscape", &flags, ImGuiMultiSelectFlags_ClearOnEscape);
3187
0
                ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ClearOnClickVoid", &flags, ImGuiMultiSelectFlags_ClearOnClickVoid);
3188
0
                if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeWindow", &flags, ImGuiMultiSelectFlags_ScopeWindow) && (flags & ImGuiMultiSelectFlags_ScopeWindow))
3189
0
                    flags &= ~ImGuiMultiSelectFlags_ScopeRect;
3190
0
                if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_ScopeRect", &flags, ImGuiMultiSelectFlags_ScopeRect) && (flags & ImGuiMultiSelectFlags_ScopeRect))
3191
0
                    flags &= ~ImGuiMultiSelectFlags_ScopeWindow;
3192
0
                if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClick", &flags, ImGuiMultiSelectFlags_SelectOnClick) && (flags & ImGuiMultiSelectFlags_SelectOnClick))
3193
0
                    flags &= ~ImGuiMultiSelectFlags_SelectOnClickRelease;
3194
0
                if (ImGui::CheckboxFlags("ImGuiMultiSelectFlags_SelectOnClickRelease", &flags, ImGuiMultiSelectFlags_SelectOnClickRelease) && (flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
3195
0
                    flags &= ~ImGuiMultiSelectFlags_SelectOnClick;
3196
0
                ImGui::SameLine(); HelpMarker("Allow dragging an unselected item without altering selection.");
3197
0
                ImGui::TreePop();
3198
0
            }
3199
3200
            // Initialize default list with 1000 items.
3201
            // Use default selection.Adapter: Pass index to SetNextItemSelectionUserData(), store index in Selection
3202
0
            static ImVector<int> items;
3203
0
            static int items_next_id = 0;
3204
0
            if (items_next_id == 0) { for (int n = 0; n < 1000; n++) { items.push_back(items_next_id++); } }
3205
0
            static ExampleSelectionWithDeletion selection;
3206
0
            static bool request_deletion_from_menu = false; // Queue deletion triggered from context menu
3207
3208
0
            ImGui::Text("Selection size: %d/%d", selection.Size, items.Size);
3209
3210
0
            const float items_height = (widget_type == WidgetType_TreeNode) ? ImGui::GetTextLineHeight() : ImGui::GetTextLineHeightWithSpacing();
3211
0
            ImGui::SetNextWindowContentSize(ImVec2(0.0f, items.Size * items_height));
3212
0
            if (ImGui::BeginChild("##Basket", ImVec2(-FLT_MIN, ImGui::GetFontSize() * 20), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY))
3213
0
            {
3214
0
                ImVec2 color_button_sz(ImGui::GetFontSize(), ImGui::GetFontSize());
3215
0
                if (widget_type == WidgetType_TreeNode)
3216
0
                    ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
3217
3218
0
                ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(flags, selection.Size, items.Size);
3219
0
                selection.ApplyRequests(ms_io);
3220
3221
0
                const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (selection.Size > 0)) || request_deletion_from_menu;
3222
0
                const int item_curr_idx_to_focus = want_delete ? selection.ApplyDeletionPreLoop(ms_io, items.Size) : -1;
3223
0
                request_deletion_from_menu = false;
3224
3225
0
                if (show_in_table)
3226
0
                {
3227
0
                    if (widget_type == WidgetType_TreeNode)
3228
0
                        ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0.0f, 0.0f));
3229
0
                    ImGui::BeginTable("##Split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoPadOuterX);
3230
0
                    ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.70f);
3231
0
                    ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 0.30f);
3232
                    //ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, 0.0f);
3233
0
                }
3234
3235
0
                ImGuiListClipper clipper;
3236
0
                if (use_clipper)
3237
0
                {
3238
0
                    clipper.Begin(items.Size);
3239
0
                    if (item_curr_idx_to_focus != -1)
3240
0
                        clipper.IncludeItemByIndex(item_curr_idx_to_focus); // Ensure focused item is not clipped.
3241
0
                    if (ms_io->RangeSrcItem != -1)
3242
0
                        clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem); // Ensure RangeSrc item is not clipped.
3243
0
                }
3244
3245
0
                while (!use_clipper || clipper.Step())
3246
0
                {
3247
0
                    const int item_begin = use_clipper ? clipper.DisplayStart : 0;
3248
0
                    const int item_end = use_clipper ? clipper.DisplayEnd : items.Size;
3249
0
                    for (int n = item_begin; n < item_end; n++)
3250
0
                    {
3251
0
                        if (show_in_table)
3252
0
                            ImGui::TableNextColumn();
3253
3254
0
                        const int item_id = items[n];
3255
0
                        const char* item_category = ExampleNames[item_id % IM_ARRAYSIZE(ExampleNames)];
3256
0
                        char label[64];
3257
0
                        sprintf(label, "Object %05d: %s", item_id, item_category);
3258
3259
                        // IMPORTANT: for deletion refocus to work we need object ID to be stable,
3260
                        // aka not depend on their index in the list. Here we use our persistent item_id
3261
                        // instead of index to build a unique ID that will persist.
3262
                        // (If we used PushID(index) instead, focus wouldn't be restored correctly after deletion).
3263
0
                        ImGui::PushID(item_id);
3264
3265
                        // Emit a color button, to test that Shift+LeftArrow landing on an item that is not part
3266
                        // of the selection scope doesn't erroneously alter our selection.
3267
0
                        if (show_color_button)
3268
0
                        {
3269
0
                            ImU32 dummy_col = (ImU32)((unsigned int)n * 0xC250B74B) | IM_COL32_A_MASK;
3270
0
                            ImGui::ColorButton("##", ImColor(dummy_col), ImGuiColorEditFlags_NoTooltip, color_button_sz);
3271
0
                            ImGui::SameLine();
3272
0
                        }
3273
3274
                        // Submit item
3275
0
                        bool item_is_selected = selection.Contains((ImGuiID)n);
3276
0
                        bool item_is_open = false;
3277
0
                        ImGui::SetNextItemSelectionUserData(n);
3278
0
                        if (widget_type == WidgetType_Selectable)
3279
0
                        {
3280
0
                            ImGui::Selectable(label, item_is_selected, ImGuiSelectableFlags_None);
3281
0
                        }
3282
0
                        else if (widget_type == WidgetType_TreeNode)
3283
0
                        {
3284
0
                            ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
3285
0
                            if (item_is_selected)
3286
0
                                tree_node_flags |= ImGuiTreeNodeFlags_Selected;
3287
0
                            item_is_open = ImGui::TreeNodeEx(label, tree_node_flags);
3288
0
                        }
3289
3290
                        // Focus (for after deletion)
3291
0
                        if (item_curr_idx_to_focus == n)
3292
0
                            ImGui::SetKeyboardFocusHere(-1);
3293
3294
                        // Drag and Drop
3295
0
                        if (use_drag_drop && ImGui::BeginDragDropSource())
3296
0
                        {
3297
                            // Create payload with full selection OR single unselected item.
3298
                            // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
3299
0
                            if (ImGui::GetDragDropPayload() == NULL)
3300
0
                            {
3301
0
                                ImVector<int> payload_items;
3302
0
                                void* it = NULL;
3303
0
                                ImGuiID id = 0;
3304
0
                                if (!item_is_selected)
3305
0
                                    payload_items.push_back(item_id);
3306
0
                                else
3307
0
                                    while (selection.GetNextSelectedItem(&it, &id))
3308
0
                                        payload_items.push_back((int)id);
3309
0
                                ImGui::SetDragDropPayload("MULTISELECT_DEMO_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
3310
0
                            }
3311
3312
                            // Display payload content in tooltip
3313
0
                            const ImGuiPayload* payload = ImGui::GetDragDropPayload();
3314
0
                            const int* payload_items = (int*)payload->Data;
3315
0
                            const int payload_count = (int)payload->DataSize / (int)sizeof(int);
3316
0
                            if (payload_count == 1)
3317
0
                                ImGui::Text("Object %05d: %s", payload_items[0], ExampleNames[payload_items[0] % IM_ARRAYSIZE(ExampleNames)]);
3318
0
                            else
3319
0
                                ImGui::Text("Dragging %d objects", payload_count);
3320
3321
0
                            ImGui::EndDragDropSource();
3322
0
                        }
3323
3324
0
                        if (widget_type == WidgetType_TreeNode && item_is_open)
3325
0
                            ImGui::TreePop();
3326
3327
                        // Right-click: context menu
3328
0
                        if (ImGui::BeginPopupContextItem())
3329
0
                        {
3330
0
                            ImGui::BeginDisabled(!use_deletion || selection.Size == 0);
3331
0
                            sprintf(label, "Delete %d item(s)###DeleteSelected", selection.Size);
3332
0
                            if (ImGui::Selectable(label))
3333
0
                                request_deletion_from_menu = true;
3334
0
                            ImGui::EndDisabled();
3335
0
                            ImGui::Selectable("Close");
3336
0
                            ImGui::EndPopup();
3337
0
                        }
3338
3339
                        // Demo content within a table
3340
0
                        if (show_in_table)
3341
0
                        {
3342
0
                            ImGui::TableNextColumn();
3343
0
                            ImGui::SetNextItemWidth(-FLT_MIN);
3344
0
                            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
3345
0
                            ImGui::InputText("###NoLabel", (char*)(void*)item_category, strlen(item_category), ImGuiInputTextFlags_ReadOnly);
3346
0
                            ImGui::PopStyleVar();
3347
0
                        }
3348
3349
0
                        ImGui::PopID();
3350
0
                    }
3351
0
                    if (!use_clipper)
3352
0
                        break;
3353
0
                }
3354
3355
0
                if (show_in_table)
3356
0
                {
3357
0
                    ImGui::EndTable();
3358
0
                    if (widget_type == WidgetType_TreeNode)
3359
0
                        ImGui::PopStyleVar();
3360
0
                }
3361
3362
                // Apply multi-select requests
3363
0
                ms_io = ImGui::EndMultiSelect();
3364
0
                selection.ApplyRequests(ms_io);
3365
0
                if (want_delete)
3366
0
                    selection.ApplyDeletionPostLoop(ms_io, items, item_curr_idx_to_focus);
3367
3368
0
                if (widget_type == WidgetType_TreeNode)
3369
0
                    ImGui::PopStyleVar();
3370
0
            }
3371
0
            ImGui::EndChild();
3372
0
            ImGui::TreePop();
3373
0
        }
3374
0
        ImGui::TreePop();
3375
0
    }
3376
0
}
3377
3378
//-----------------------------------------------------------------------------
3379
// [SECTION] DemoWindowWidgetsTabs()
3380
//-----------------------------------------------------------------------------
3381
3382
static void EditTabBarFittingPolicyFlags(ImGuiTabBarFlags* p_flags)
3383
0
{
3384
0
    if ((*p_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
3385
0
        *p_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
3386
0
    if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyMixed", p_flags, ImGuiTabBarFlags_FittingPolicyMixed))
3387
0
        *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyMixed);
3388
0
    if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyShrink", p_flags, ImGuiTabBarFlags_FittingPolicyShrink))
3389
0
        *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyShrink);
3390
0
    if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", p_flags, ImGuiTabBarFlags_FittingPolicyScroll))
3391
0
        *p_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);
3392
0
}
3393
3394
static void DemoWindowWidgetsTabs()
3395
0
{
3396
0
    IMGUI_DEMO_MARKER("Widgets/Tabs");
3397
0
    if (ImGui::TreeNode("Tabs"))
3398
0
    {
3399
0
        IMGUI_DEMO_MARKER("Widgets/Tabs/Basic");
3400
0
        if (ImGui::TreeNode("Basic"))
3401
0
        {
3402
0
            ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;
3403
0
            if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3404
0
            {
3405
0
                if (ImGui::BeginTabItem("Avocado"))
3406
0
                {
3407
0
                    ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah");
3408
0
                    ImGui::EndTabItem();
3409
0
                }
3410
0
                if (ImGui::BeginTabItem("Broccoli"))
3411
0
                {
3412
0
                    ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah");
3413
0
                    ImGui::EndTabItem();
3414
0
                }
3415
0
                if (ImGui::BeginTabItem("Cucumber"))
3416
0
                {
3417
0
                    ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah");
3418
0
                    ImGui::EndTabItem();
3419
0
                }
3420
0
                ImGui::EndTabBar();
3421
0
            }
3422
0
            ImGui::Separator();
3423
0
            ImGui::TreePop();
3424
0
        }
3425
3426
0
        IMGUI_DEMO_MARKER("Widgets/Tabs/Advanced & Close Button");
3427
0
        if (ImGui::TreeNode("Advanced & Close Button"))
3428
0
        {
3429
            // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).
3430
0
            static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;
3431
0
            ImGui::CheckboxFlags("ImGuiTabBarFlags_Reorderable", &tab_bar_flags, ImGuiTabBarFlags_Reorderable);
3432
0
            ImGui::CheckboxFlags("ImGuiTabBarFlags_AutoSelectNewTabs", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs);
3433
0
            ImGui::CheckboxFlags("ImGuiTabBarFlags_TabListPopupButton", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);
3434
0
            ImGui::CheckboxFlags("ImGuiTabBarFlags_NoCloseWithMiddleMouseButton", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);
3435
0
            ImGui::CheckboxFlags("ImGuiTabBarFlags_DrawSelectedOverline", &tab_bar_flags, ImGuiTabBarFlags_DrawSelectedOverline);
3436
0
            EditTabBarFittingPolicyFlags(&tab_bar_flags);
3437
3438
            // Tab Bar
3439
0
            ImGui::AlignTextToFramePadding();
3440
0
            ImGui::Text("Opened:");
3441
0
            const char* names[4] = { "Artichoke", "Beetroot", "Celery", "Daikon" };
3442
0
            static bool opened[4] = { true, true, true, true }; // Persistent user state
3443
0
            for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3444
0
            {
3445
0
                ImGui::SameLine();
3446
0
                ImGui::Checkbox(names[n], &opened[n]);
3447
0
            }
3448
3449
            // Passing a bool* to BeginTabItem() is similar to passing one to Begin():
3450
            // the underlying bool will be set to false when the tab is closed.
3451
0
            if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3452
0
            {
3453
0
                for (int n = 0; n < IM_ARRAYSIZE(opened); n++)
3454
0
                    if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None))
3455
0
                    {
3456
0
                        ImGui::Text("This is the %s tab!", names[n]);
3457
0
                        if (n & 1)
3458
0
                            ImGui::Text("I am an odd tab.");
3459
0
                        ImGui::EndTabItem();
3460
0
                    }
3461
0
                ImGui::EndTabBar();
3462
0
            }
3463
0
            ImGui::Separator();
3464
0
            ImGui::TreePop();
3465
0
        }
3466
3467
0
        IMGUI_DEMO_MARKER("Widgets/Tabs/TabItemButton & Leading-Trailing flags");
3468
0
        if (ImGui::TreeNode("TabItemButton & Leading/Trailing flags"))
3469
0
        {
3470
0
            static ImVector<int> active_tabs;
3471
0
            static int next_tab_id = 0;
3472
0
            if (next_tab_id == 0) // Initialize with some default tabs
3473
0
                for (int i = 0; i < 3; i++)
3474
0
                    active_tabs.push_back(next_tab_id++);
3475
3476
            // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.
3477
            // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...
3478
            // but they tend to make more sense together)
3479
0
            static bool show_leading_button = true;
3480
0
            static bool show_trailing_button = true;
3481
0
            ImGui::Checkbox("Show Leading TabItemButton()", &show_leading_button);
3482
0
            ImGui::Checkbox("Show Trailing TabItemButton()", &show_trailing_button);
3483
3484
            // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs
3485
0
            static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyShrink;
3486
0
            EditTabBarFittingPolicyFlags(&tab_bar_flags);
3487
3488
0
            if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags))
3489
0
            {
3490
                // Demo a Leading TabItemButton(): click the "?" button to open a menu
3491
0
                if (show_leading_button)
3492
0
                    if (ImGui::TabItemButton("?", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))
3493
0
                        ImGui::OpenPopup("MyHelpMenu");
3494
0
                if (ImGui::BeginPopup("MyHelpMenu"))
3495
0
                {
3496
0
                    ImGui::Selectable("Hello!");
3497
0
                    ImGui::EndPopup();
3498
0
                }
3499
3500
                // Demo Trailing Tabs: click the "+" button to add a new tab.
3501
                // (In your app you may want to use a font icon instead of the "+")
3502
                // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end.
3503
0
                if (show_trailing_button)
3504
0
                    if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))
3505
0
                        active_tabs.push_back(next_tab_id++); // Add new tab
3506
3507
                // Submit our regular tabs
3508
0
                for (int n = 0; n < active_tabs.Size; )
3509
0
                {
3510
0
                    bool open = true;
3511
0
                    char name[16];
3512
0
                    snprintf(name, IM_ARRAYSIZE(name), "%04d", active_tabs[n]);
3513
0
                    if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None))
3514
0
                    {
3515
0
                        ImGui::Text("This is the %s tab!", name);
3516
0
                        ImGui::EndTabItem();
3517
0
                    }
3518
3519
0
                    if (!open)
3520
0
                        active_tabs.erase(active_tabs.Data + n);
3521
0
                    else
3522
0
                        n++;
3523
0
                }
3524
3525
0
                ImGui::EndTabBar();
3526
0
            }
3527
0
            ImGui::Separator();
3528
0
            ImGui::TreePop();
3529
0
        }
3530
0
        ImGui::TreePop();
3531
0
    }
3532
0
}
3533
3534
//-----------------------------------------------------------------------------
3535
// [SECTION] DemoWindowWidgetsText()
3536
//-----------------------------------------------------------------------------
3537
3538
static void DemoWindowWidgetsText()
3539
0
{
3540
0
    IMGUI_DEMO_MARKER("Widgets/Text");
3541
0
    if (ImGui::TreeNode("Text"))
3542
0
    {
3543
0
        IMGUI_DEMO_MARKER("Widgets/Text/Colored Text");
3544
0
        if (ImGui::TreeNode("Colorful Text"))
3545
0
        {
3546
            // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.
3547
0
            ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), "Pink");
3548
0
            ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Yellow");
3549
0
            ImGui::TextDisabled("Disabled");
3550
0
            ImGui::SameLine(); HelpMarker("The TextDisabled color is stored in ImGuiStyle.");
3551
0
            ImGui::TreePop();
3552
0
        }
3553
3554
0
        IMGUI_DEMO_MARKER("Widgets/Text/Font Size");
3555
0
        if (ImGui::TreeNode("Font Size"))
3556
0
        {
3557
0
            ImGuiStyle& style = ImGui::GetStyle();
3558
0
            const float global_scale = style.FontScaleMain * style.FontScaleDpi;
3559
0
            ImGui::Text("style.FontScaleMain = %0.2f", style.FontScaleMain);
3560
0
            ImGui::Text("style.FontScaleDpi = %0.2f", style.FontScaleDpi);
3561
0
            ImGui::Text("global_scale = ~%0.2f", global_scale); // This is not technically accurate as internal scales may apply, but conceptually let's pretend it is.
3562
0
            ImGui::Text("FontSize = %0.2f", ImGui::GetFontSize());
3563
3564
0
            ImGui::SeparatorText("");
3565
0
            static float custom_size = 16.0f;
3566
0
            ImGui::SliderFloat("custom_size", &custom_size, 10.0f, 100.0f, "%.0f");
3567
0
            ImGui::Text("ImGui::PushFont(nullptr, custom_size);");
3568
0
            ImGui::PushFont(NULL, custom_size);
3569
0
            ImGui::Text("FontSize = %.2f (== %.2f * global_scale)", ImGui::GetFontSize(), custom_size);
3570
0
            ImGui::PopFont();
3571
3572
0
            ImGui::SeparatorText("");
3573
0
            static float custom_scale = 1.0f;
3574
0
            ImGui::SliderFloat("custom_scale", &custom_scale, 0.5f, 4.0f, "%.2f");
3575
0
            ImGui::Text("ImGui::PushFont(nullptr, style.FontSizeBase * custom_scale);");
3576
0
            ImGui::PushFont(NULL, style.FontSizeBase * custom_scale);
3577
0
            ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), custom_scale);
3578
0
            ImGui::PopFont();
3579
3580
0
            ImGui::SeparatorText("");
3581
0
            for (float scaling = 0.5f; scaling <= 4.0f; scaling += 0.5f)
3582
0
            {
3583
0
                ImGui::PushFont(NULL, style.FontSizeBase * scaling);
3584
0
                ImGui::Text("FontSize = %.2f (== style.FontSizeBase * %.2f * global_scale)", ImGui::GetFontSize(), scaling);
3585
0
                ImGui::PopFont();
3586
0
            }
3587
3588
0
            ImGui::TreePop();
3589
0
        }
3590
3591
0
        IMGUI_DEMO_MARKER("Widgets/Text/Word Wrapping");
3592
0
        if (ImGui::TreeNode("Word Wrapping"))
3593
0
        {
3594
            // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.
3595
0
            ImGui::TextWrapped(
3596
0
                "This text should automatically wrap on the edge of the window. The current implementation "
3597
0
                "for text wrapping follows simple rules suitable for English and possibly other languages.");
3598
0
            ImGui::Spacing();
3599
3600
0
            static float wrap_width = 200.0f;
3601
0
            ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f");
3602
3603
0
            ImDrawList* draw_list = ImGui::GetWindowDrawList();
3604
0
            for (int n = 0; n < 2; n++)
3605
0
            {
3606
0
                ImGui::Text("Test paragraph %d:", n);
3607
0
                ImVec2 pos = ImGui::GetCursorScreenPos();
3608
0
                ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);
3609
0
                ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());
3610
0
                ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);
3611
0
                if (n == 0)
3612
0
                    ImGui::Text("The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width);
3613
0
                else
3614
0
                    ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee   ffffffff. gggggggg!hhhhhhhh");
3615
3616
                // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)
3617
0
                draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));
3618
0
                draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));
3619
0
                ImGui::PopTextWrapPos();
3620
0
            }
3621
3622
0
            ImGui::TreePop();
3623
0
        }
3624
3625
0
        IMGUI_DEMO_MARKER("Widgets/Text/UTF-8 Text");
3626
0
        if (ImGui::TreeNode("UTF-8 Text"))
3627
0
        {
3628
            // UTF-8 test with Japanese characters
3629
            // (Needs a suitable font? Try "Google Noto" or "Arial Unicode". See docs/FONTS.md for details.)
3630
            // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8
3631
            // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you
3632
            //   can save your source files as 'UTF-8 without signature').
3633
            // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8
3634
            //   CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.
3635
            //   Don't do this in your application! Please use u8"text in any language" in your application!
3636
            // Note that characters values are preserved even by InputText() if the font cannot be displayed,
3637
            // so you can safely copy & paste garbled characters into another application.
3638
0
            ImGui::TextWrapped(
3639
0
                "CJK text will only appear if the font was loaded with the appropriate CJK character ranges. "
3640
0
                "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. "
3641
0
                "Read docs/FONTS.md for details.");
3642
0
            ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)");
3643
0
            ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)");
3644
0
            static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e";
3645
            //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis
3646
0
            ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf));
3647
0
            ImGui::TreePop();
3648
0
        }
3649
0
        ImGui::TreePop();
3650
0
    }
3651
0
}
3652
3653
//-----------------------------------------------------------------------------
3654
// [SECTION] DemoWindowWidgetsTextFilter()
3655
//-----------------------------------------------------------------------------
3656
3657
static void DemoWindowWidgetsTextFilter()
3658
0
{
3659
0
    IMGUI_DEMO_MARKER("Widgets/Text Filter");
3660
0
    if (ImGui::TreeNode("Text Filter"))
3661
0
    {
3662
        // Helper class to easy setup a text filter.
3663
        // You may want to implement a more feature-full filtering scheme in your own application.
3664
0
        HelpMarker("Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.");
3665
0
        static ImGuiTextFilter filter;
3666
0
        ImGui::Text("Filter usage:\n"
3667
0
            "  \"\"         display all lines\n"
3668
0
            "  \"xxx\"      display lines containing \"xxx\"\n"
3669
0
            "  \"xxx,yyy\"  display lines containing \"xxx\" or \"yyy\"\n"
3670
0
            "  \"-xxx\"     hide lines containing \"xxx\"");
3671
0
        filter.Draw();
3672
0
        const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" };
3673
0
        for (int i = 0; i < IM_ARRAYSIZE(lines); i++)
3674
0
            if (filter.PassFilter(lines[i]))
3675
0
                ImGui::BulletText("%s", lines[i]);
3676
0
        ImGui::TreePop();
3677
0
    }
3678
0
}
3679
3680
//-----------------------------------------------------------------------------
3681
// [SECTION] DemoWindowWidgetsTextInput()
3682
//-----------------------------------------------------------------------------
3683
3684
static void DemoWindowWidgetsTextInput()
3685
0
{
3686
    // To wire InputText() with std::string or any other custom string type,
3687
    // see the "Text Input > Resize Callback" section of this demo, and the misc/cpp/imgui_stdlib.h file.
3688
0
    IMGUI_DEMO_MARKER("Widgets/Text Input");
3689
0
    if (ImGui::TreeNode("Text Input"))
3690
0
    {
3691
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Multi-line Text Input");
3692
0
        if (ImGui::TreeNode("Multi-line Text Input"))
3693
0
        {
3694
            // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize
3695
            // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.
3696
0
            static char text[1024 * 16] =
3697
0
                "/*\n"
3698
0
                " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n"
3699
0
                " the hexadecimal encoding of one offending instruction,\n"
3700
0
                " more formally, the invalid operand with locked CMPXCHG8B\n"
3701
0
                " instruction bug, is a design flaw in the majority of\n"
3702
0
                " Intel Pentium, Pentium MMX, and Pentium OverDrive\n"
3703
0
                " processors (all in the P5 microarchitecture).\n"
3704
0
                "*/\n\n"
3705
0
                "label:\n"
3706
0
                "\tlock cmpxchg8b eax\n";
3707
3708
0
            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;
3709
0
            HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)");
3710
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
3711
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
3712
0
            ImGui::SameLine(); HelpMarker("Feature is currently in Beta. Please read comments in imgui.h");
3713
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput);
3714
0
            ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets.");
3715
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);
3716
0
            ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
3717
0
            ImGui::TreePop();
3718
0
        }
3719
3720
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Filtered Text Input");
3721
0
        if (ImGui::TreeNode("Filtered Text Input"))
3722
0
        {
3723
0
            struct TextFilters
3724
0
            {
3725
                // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)
3726
0
                static int FilterCasingSwap(ImGuiInputTextCallbackData* data)
3727
0
                {
3728
0
                    if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase
3729
0
                    else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase
3730
0
                    return 0;
3731
0
                }
3732
3733
                // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)
3734
0
                static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)
3735
0
                {
3736
0
                    if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar))
3737
0
                        return 0;
3738
0
                    return 1;
3739
0
                }
3740
0
            };
3741
3742
0
            static char buf1[32] = ""; ImGui::InputText("default", buf1, IM_ARRAYSIZE(buf1));
3743
0
            static char buf2[32] = ""; ImGui::InputText("decimal", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CharsDecimal);
3744
0
            static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
3745
0
            static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, IM_ARRAYSIZE(buf4), ImGuiInputTextFlags_CharsUppercase);
3746
0
            static char buf5[32] = ""; ImGui::InputText("no blank", buf5, IM_ARRAYSIZE(buf5), ImGuiInputTextFlags_CharsNoBlank);
3747
0
            static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, IM_ARRAYSIZE(buf6), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.
3748
0
            static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, IM_ARRAYSIZE(buf7), ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.
3749
0
            ImGui::TreePop();
3750
0
        }
3751
3752
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Password input");
3753
0
        if (ImGui::TreeNode("Password Input"))
3754
0
        {
3755
0
            static char password[64] = "password123";
3756
0
            ImGui::InputText("password", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
3757
0
            ImGui::SameLine(); HelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n");
3758
0
            ImGui::InputTextWithHint("password (w/ hint)", "<password>", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);
3759
0
            ImGui::InputText("password (clear)", password, IM_ARRAYSIZE(password));
3760
0
            ImGui::TreePop();
3761
0
        }
3762
3763
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks");
3764
0
        if (ImGui::TreeNode("Completion, History, Edit Callbacks"))
3765
0
        {
3766
0
            struct Funcs
3767
0
            {
3768
0
                static int MyCallback(ImGuiInputTextCallbackData* data)
3769
0
                {
3770
0
                    if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)
3771
0
                    {
3772
0
                        data->InsertChars(data->CursorPos, "..");
3773
0
                    }
3774
0
                    else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
3775
0
                    {
3776
0
                        if (data->EventKey == ImGuiKey_UpArrow)
3777
0
                        {
3778
0
                            data->DeleteChars(0, data->BufTextLen);
3779
0
                            data->InsertChars(0, "Pressed Up!");
3780
0
                            data->SelectAll();
3781
0
                        }
3782
0
                        else if (data->EventKey == ImGuiKey_DownArrow)
3783
0
                        {
3784
0
                            data->DeleteChars(0, data->BufTextLen);
3785
0
                            data->InsertChars(0, "Pressed Down!");
3786
0
                            data->SelectAll();
3787
0
                        }
3788
0
                    }
3789
0
                    else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)
3790
0
                    {
3791
                        // Toggle casing of first character
3792
0
                        char c = data->Buf[0];
3793
0
                        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;
3794
0
                        data->BufDirty = true;
3795
3796
                        // Increment a counter
3797
0
                        int* p_int = (int*)data->UserData;
3798
0
                        *p_int = *p_int + 1;
3799
0
                    }
3800
0
                    return 0;
3801
0
                }
3802
0
            };
3803
0
            static char buf1[64];
3804
0
            ImGui::InputText("Completion", buf1, IM_ARRAYSIZE(buf1), ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);
3805
0
            ImGui::SameLine(); HelpMarker(
3806
0
                "Here we append \"..\" each time Tab is pressed. "
3807
0
                "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3808
3809
0
            static char buf2[64];
3810
0
            ImGui::InputText("History", buf2, IM_ARRAYSIZE(buf2), ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);
3811
0
            ImGui::SameLine(); HelpMarker(
3812
0
                "Here we replace and select text each time Up/Down are pressed. "
3813
0
                "See 'Examples>Console' for a more meaningful demonstration of using this callback.");
3814
3815
0
            static char buf3[64];
3816
0
            static int edit_count = 0;
3817
0
            ImGui::InputText("Edit", buf3, IM_ARRAYSIZE(buf3), ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);
3818
0
            ImGui::SameLine(); HelpMarker(
3819
0
                "Here we toggle the casing of the first character on every edit + count edits.");
3820
0
            ImGui::SameLine(); ImGui::Text("(%d)", edit_count);
3821
3822
0
            ImGui::TreePop();
3823
0
        }
3824
3825
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Resize Callback");
3826
0
        if (ImGui::TreeNode("Resize Callback"))
3827
0
        {
3828
            // To wire InputText() with std::string or any other custom string type,
3829
            // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper
3830
            // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.
3831
0
            HelpMarker(
3832
0
                "Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\n\n"
3833
0
                "See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.");
3834
0
            struct Funcs
3835
0
            {
3836
0
                static int MyResizeCallback(ImGuiInputTextCallbackData* data)
3837
0
                {
3838
0
                    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
3839
0
                    {
3840
0
                        ImVector<char>* my_str = (ImVector<char>*)data->UserData;
3841
0
                        IM_ASSERT(my_str->begin() == data->Buf);
3842
0
                        my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1
3843
0
                        data->Buf = my_str->begin();
3844
0
                    }
3845
0
                    return 0;
3846
0
                }
3847
3848
                // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.
3849
                // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'
3850
0
                static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)
3851
0
                {
3852
0
                    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
3853
0
                    return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str);
3854
0
                }
3855
0
            };
3856
3857
            // For this demo we are using ImVector as a string container.
3858
            // Note that because we need to store a terminating zero character, our size/capacity are 1 more
3859
            // than usually reported by a typical string class.
3860
0
            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_None;
3861
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_WordWrap", &flags, ImGuiInputTextFlags_WordWrap);
3862
3863
0
            static ImVector<char> my_str;
3864
0
            if (my_str.empty())
3865
0
                my_str.push_back(0);
3866
0
            Funcs::MyInputTextMultiline("##MyStr", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);
3867
0
            ImGui::Text("Data: %p\nSize: %d\nCapacity: %d", (void*)my_str.begin(), my_str.size(), my_str.capacity());
3868
0
            ImGui::TreePop();
3869
0
        }
3870
3871
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Eliding, Alignment");
3872
0
        if (ImGui::TreeNode("Eliding, Alignment"))
3873
0
        {
3874
0
            static char buf1[128] = "/path/to/some/folder/with/long/filename.cpp";
3875
0
            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_ElideLeft;
3876
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_ElideLeft", &flags, ImGuiInputTextFlags_ElideLeft);
3877
0
            ImGui::InputText("Path", buf1, IM_ARRAYSIZE(buf1), flags);
3878
0
            ImGui::TreePop();
3879
0
        }
3880
3881
0
        IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous");
3882
0
        if (ImGui::TreeNode("Miscellaneous"))
3883
0
        {
3884
0
            static char buf1[16];
3885
0
            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;
3886
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll);
3887
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly);
3888
0
            ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo);
3889
0
            ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags);
3890
0
            ImGui::TreePop();
3891
0
        }
3892
3893
0
        ImGui::TreePop();
3894
0
    }
3895
3896
0
}
3897
3898
//-----------------------------------------------------------------------------
3899
// [SECTION] DemoWindowWidgetsTooltips()
3900
//-----------------------------------------------------------------------------
3901
3902
static void DemoWindowWidgetsTooltips()
3903
0
{
3904
0
    IMGUI_DEMO_MARKER("Widgets/Tooltips");
3905
0
    if (ImGui::TreeNode("Tooltips"))
3906
0
    {
3907
        // Tooltips are windows following the mouse. They do not take focus away.
3908
0
        ImGui::SeparatorText("General");
3909
3910
        // Typical use cases:
3911
        // - Short-form (text only):      SetItemTooltip("Hello");
3912
        // - Short-form (any contents):   if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); }
3913
3914
        // - Full-form (text only):       if (IsItemHovered(...)) { SetTooltip("Hello"); }
3915
        // - Full-form (any contents):    if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); }
3916
3917
0
        HelpMarker(
3918
0
            "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n"
3919
0
            "We provide a helper SetItemTooltip() function to perform the two with standards flags.");
3920
3921
0
        ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);
3922
3923
0
        ImGui::Button("Basic", sz);
3924
0
        ImGui::SetItemTooltip("I am a tooltip");
3925
3926
0
        ImGui::Button("Fancy", sz);
3927
0
        if (ImGui::BeginItemTooltip())
3928
0
        {
3929
0
            ImGui::Text("I am a fancy tooltip");
3930
0
            static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };
3931
0
            ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr));
3932
0
            ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime()));
3933
0
            ImGui::EndTooltip();
3934
0
        }
3935
3936
0
        ImGui::SeparatorText("Always On");
3937
3938
        // Showcase NOT relying on a IsItemHovered() to emit a tooltip.
3939
        // Here the tooltip is always emitted when 'always_on == true'.
3940
0
        static int always_on = 0;
3941
0
        ImGui::RadioButton("Off", &always_on, 0);
3942
0
        ImGui::SameLine();
3943
0
        ImGui::RadioButton("Always On (Simple)", &always_on, 1);
3944
0
        ImGui::SameLine();
3945
0
        ImGui::RadioButton("Always On (Advanced)", &always_on, 2);
3946
0
        if (always_on == 1)
3947
0
            ImGui::SetTooltip("I am following you around.");
3948
0
        else if (always_on == 2 && ImGui::BeginTooltip())
3949
0
        {
3950
0
            ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f));
3951
0
            ImGui::EndTooltip();
3952
0
        }
3953
3954
0
        ImGui::SeparatorText("Custom");
3955
3956
0
        HelpMarker(
3957
0
            "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize "
3958
0
            "tooltip activation details across your application. You may however decide to use custom "
3959
0
            "flags for a specific tooltip instance.");
3960
3961
        // The following examples are passed for documentation purpose but may not be useful to most users.
3962
        // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from
3963
        // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or keyboard/gamepad is being used.
3964
        // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.
3965
0
        ImGui::Button("Manual", sz);
3966
0
        if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
3967
0
            ImGui::SetTooltip("I am a manually emitted tooltip.");
3968
3969
0
        ImGui::Button("DelayNone", sz);
3970
0
        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone))
3971
0
            ImGui::SetTooltip("I am a tooltip with no delay.");
3972
3973
0
        ImGui::Button("DelayShort", sz);
3974
0
        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))
3975
0
            ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort);
3976
3977
0
        ImGui::Button("DelayLong", sz);
3978
0
        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))
3979
0
            ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal);
3980
3981
0
        ImGui::Button("Stationary", sz);
3982
0
        if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))
3983
0
            ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating.");
3984
3985
        // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',
3986
        // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.
3987
0
        ImGui::BeginDisabled();
3988
0
        ImGui::Button("Disabled item", sz);
3989
0
        if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
3990
0
            ImGui::SetTooltip("I am a a tooltip for a disabled item.");
3991
0
        ImGui::EndDisabled();
3992
3993
0
        ImGui::TreePop();
3994
0
    }
3995
0
}
3996
3997
//-----------------------------------------------------------------------------
3998
// [SECTION] DemoWindowWidgetsTreeNodes()
3999
//-----------------------------------------------------------------------------
4000
4001
static void DemoWindowWidgetsTreeNodes()
4002
0
{
4003
0
    IMGUI_DEMO_MARKER("Widgets/Tree Nodes");
4004
0
    if (ImGui::TreeNode("Tree Nodes"))
4005
0
    {
4006
        // See see "Examples -> Property Editor" (ShowExampleAppPropertyEditor() function) for a fancier, data-driven tree.
4007
0
        IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees");
4008
0
        if (ImGui::TreeNode("Basic trees"))
4009
0
        {
4010
0
            for (int i = 0; i < 5; i++)
4011
0
            {
4012
                // Use SetNextItemOpen() so set the default state of a node to be open. We could
4013
                // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!
4014
0
                if (i == 0)
4015
0
                    ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4016
4017
                // Here we use PushID() to generate a unique base ID, and then the "" used as TreeNode id won't conflict.
4018
                // An alternative to using 'PushID() + TreeNode("", ...)' to generate a unique ID is to use 'TreeNode((void*)(intptr_t)i, ...)',
4019
                // aka generate a dummy pointer-sized value to be hashed. The demo below uses that technique. Both are fine.
4020
0
                ImGui::PushID(i);
4021
0
                if (ImGui::TreeNode("", "Child %d", i))
4022
0
                {
4023
0
                    ImGui::Text("blah blah");
4024
0
                    ImGui::SameLine();
4025
0
                    if (ImGui::SmallButton("button")) {}
4026
0
                    ImGui::TreePop();
4027
0
                }
4028
0
                ImGui::PopID();
4029
0
            }
4030
0
            ImGui::TreePop();
4031
0
        }
4032
4033
0
        IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Hierarchy lines");
4034
0
        if (ImGui::TreeNode("Hierarchy lines"))
4035
0
        {
4036
0
            static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DefaultOpen;
4037
0
            HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4038
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
4039
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
4040
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
4041
4042
0
            if (ImGui::TreeNodeEx("Parent", base_flags))
4043
0
            {
4044
0
                if (ImGui::TreeNodeEx("Child 1", base_flags))
4045
0
                {
4046
0
                    ImGui::Button("Button for Child 1");
4047
0
                    ImGui::TreePop();
4048
0
                }
4049
0
                if (ImGui::TreeNodeEx("Child 2", base_flags))
4050
0
                {
4051
0
                    ImGui::Button("Button for Child 2");
4052
0
                    ImGui::TreePop();
4053
0
                }
4054
0
                ImGui::Text("Remaining contents");
4055
0
                ImGui::Text("Remaining contents");
4056
0
                ImGui::TreePop();
4057
0
            }
4058
4059
0
            ImGui::TreePop();
4060
0
        }
4061
4062
0
        IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes");
4063
0
        if (ImGui::TreeNode("Advanced, with Selectable nodes"))
4064
0
        {
4065
0
            HelpMarker(
4066
0
                "This is a more typical looking tree with selectable nodes.\n"
4067
0
                "Click to select, CTRL+Click to toggle, click on arrows or double-click to open.");
4068
0
            static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
4069
0
            static bool align_label_with_current_x_position = false;
4070
0
            static bool test_drag_and_drop = false;
4071
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
4072
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);
4073
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node.");
4074
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
4075
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth", &base_flags, ImGuiTreeNodeFlags_SpanLabelWidth); ImGui::SameLine(); HelpMarker("Reduce hit area to the text label and a bit of margin.");
4076
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only.");
4077
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_AllowOverlap", &base_flags, ImGuiTreeNodeFlags_AllowOverlap);
4078
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_Framed", &base_flags, ImGuiTreeNodeFlags_Framed); ImGui::SameLine(); HelpMarker("Draw frame with background (e.g. for CollapsingHeader)");
4079
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_NavLeftJumpsToParent", &base_flags, ImGuiTreeNodeFlags_NavLeftJumpsToParent);
4080
4081
0
            HelpMarker("Default option for DrawLinesXXX is stored in style.TreeLinesFlags");
4082
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesNone", &base_flags, ImGuiTreeNodeFlags_DrawLinesNone);
4083
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesFull", &base_flags, ImGuiTreeNodeFlags_DrawLinesFull);
4084
0
            ImGui::CheckboxFlags("ImGuiTreeNodeFlags_DrawLinesToNodes", &base_flags, ImGuiTreeNodeFlags_DrawLinesToNodes);
4085
4086
0
            ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position);
4087
0
            ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop);
4088
0
            ImGui::Text("Hello!");
4089
0
            if (align_label_with_current_x_position)
4090
0
                ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
4091
4092
            // 'selection_mask' is dumb representation of what may be user-side selection state.
4093
            //  You may retain selection state inside or outside your objects in whatever format you see fit.
4094
            // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end
4095
            /// of the loop. May be a pointer to your own node type, etc.
4096
0
            static int selection_mask = (1 << 2);
4097
0
            int node_clicked = -1;
4098
0
            for (int i = 0; i < 6; i++)
4099
0
            {
4100
                // Disable the default "open on single-click behavior" + set Selected flag according to our selection.
4101
                // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.
4102
0
                ImGuiTreeNodeFlags node_flags = base_flags;
4103
0
                const bool is_selected = (selection_mask & (1 << i)) != 0;
4104
0
                if (is_selected)
4105
0
                    node_flags |= ImGuiTreeNodeFlags_Selected;
4106
0
                if (i < 3)
4107
0
                {
4108
                    // Items 0..2 are Tree Node
4109
0
                    bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
4110
0
                    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4111
0
                        node_clicked = i;
4112
0
                    if (test_drag_and_drop && ImGui::BeginDragDropSource())
4113
0
                    {
4114
0
                        ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
4115
0
                        ImGui::Text("This is a drag and drop source");
4116
0
                        ImGui::EndDragDropSource();
4117
0
                    }
4118
0
                    if (i == 2 && (base_flags & ImGuiTreeNodeFlags_SpanLabelWidth))
4119
0
                    {
4120
                        // Item 2 has an additional inline button to help demonstrate SpanLabelWidth.
4121
0
                        ImGui::SameLine();
4122
0
                        if (ImGui::SmallButton("button")) {}
4123
0
                    }
4124
0
                    if (node_open)
4125
0
                    {
4126
0
                        ImGui::BulletText("Blah blah\nBlah Blah");
4127
0
                        ImGui::SameLine();
4128
0
                        ImGui::SmallButton("Button");
4129
0
                        ImGui::TreePop();
4130
0
                    }
4131
0
                }
4132
0
                else
4133
0
                {
4134
                    // Items 3..5 are Tree Leaves
4135
                    // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can
4136
                    // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().
4137
0
                    node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet
4138
0
                    ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i);
4139
0
                    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())
4140
0
                        node_clicked = i;
4141
0
                    if (test_drag_and_drop && ImGui::BeginDragDropSource())
4142
0
                    {
4143
0
                        ImGui::SetDragDropPayload("_TREENODE", NULL, 0);
4144
0
                        ImGui::Text("This is a drag and drop source");
4145
0
                        ImGui::EndDragDropSource();
4146
0
                    }
4147
0
                }
4148
0
            }
4149
0
            if (node_clicked != -1)
4150
0
            {
4151
                // Update selection state
4152
                // (process outside of tree loop to avoid visual inconsistencies during the clicking frame)
4153
0
                if (ImGui::GetIO().KeyCtrl)
4154
0
                    selection_mask ^= (1 << node_clicked);          // CTRL+click to toggle
4155
0
                else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection
4156
0
                    selection_mask = (1 << node_clicked);           // Click to single-select
4157
0
            }
4158
0
            if (align_label_with_current_x_position)
4159
0
                ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
4160
0
            ImGui::TreePop();
4161
0
        }
4162
0
        ImGui::TreePop();
4163
0
    }
4164
0
}
4165
4166
//-----------------------------------------------------------------------------
4167
// [SECTION] DemoWindowWidgetsVerticalSliders()
4168
//-----------------------------------------------------------------------------
4169
4170
static void DemoWindowWidgetsVerticalSliders()
4171
0
{
4172
0
    IMGUI_DEMO_MARKER("Widgets/Vertical Sliders");
4173
0
    if (ImGui::TreeNode("Vertical Sliders"))
4174
0
    {
4175
0
        const float spacing = 4;
4176
0
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));
4177
4178
0
        static int int_value = 0;
4179
0
        ImGui::VSliderInt("##int", ImVec2(18, 160), &int_value, 0, 5);
4180
0
        ImGui::SameLine();
4181
4182
0
        static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };
4183
0
        ImGui::PushID("set1");
4184
0
        for (int i = 0; i < 7; i++)
4185
0
        {
4186
0
            if (i > 0) ImGui::SameLine();
4187
0
            ImGui::PushID(i);
4188
0
            ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));
4189
0
            ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));
4190
0
            ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));
4191
0
            ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));
4192
0
            ImGui::VSliderFloat("##v", ImVec2(18, 160), &values[i], 0.0f, 1.0f, "");
4193
0
            if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4194
0
                ImGui::SetTooltip("%.3f", values[i]);
4195
0
            ImGui::PopStyleColor(4);
4196
0
            ImGui::PopID();
4197
0
        }
4198
0
        ImGui::PopID();
4199
4200
0
        ImGui::SameLine();
4201
0
        ImGui::PushID("set2");
4202
0
        static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };
4203
0
        const int rows = 3;
4204
0
        const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));
4205
0
        for (int nx = 0; nx < 4; nx++)
4206
0
        {
4207
0
            if (nx > 0) ImGui::SameLine();
4208
0
            ImGui::BeginGroup();
4209
0
            for (int ny = 0; ny < rows; ny++)
4210
0
            {
4211
0
                ImGui::PushID(nx * rows + ny);
4212
0
                ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, "");
4213
0
                if (ImGui::IsItemActive() || ImGui::IsItemHovered())
4214
0
                    ImGui::SetTooltip("%.3f", values2[nx]);
4215
0
                ImGui::PopID();
4216
0
            }
4217
0
            ImGui::EndGroup();
4218
0
        }
4219
0
        ImGui::PopID();
4220
4221
0
        ImGui::SameLine();
4222
0
        ImGui::PushID("set3");
4223
0
        for (int i = 0; i < 4; i++)
4224
0
        {
4225
0
            if (i > 0) ImGui::SameLine();
4226
0
            ImGui::PushID(i);
4227
0
            ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);
4228
0
            ImGui::VSliderFloat("##v", ImVec2(40, 160), &values[i], 0.0f, 1.0f, "%.2f\nsec");
4229
0
            ImGui::PopStyleVar();
4230
0
            ImGui::PopID();
4231
0
        }
4232
0
        ImGui::PopID();
4233
0
        ImGui::PopStyleVar();
4234
0
        ImGui::TreePop();
4235
0
    }
4236
0
}
4237
4238
//-----------------------------------------------------------------------------
4239
// [SECTION] DemoWindowWidgets()
4240
//-----------------------------------------------------------------------------
4241
4242
static void DemoWindowWidgets(ImGuiDemoWindowData* demo_data)
4243
0
{
4244
0
    IMGUI_DEMO_MARKER("Widgets");
4245
    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
4246
0
    if (!ImGui::CollapsingHeader("Widgets"))
4247
0
        return;
4248
4249
0
    const bool disable_all = demo_data->DisableSections; // The Checkbox for that is inside the "Disabled" section at the bottom
4250
0
    if (disable_all)
4251
0
        ImGui::BeginDisabled();
4252
4253
0
    DemoWindowWidgetsBasic();
4254
0
    DemoWindowWidgetsBullets();
4255
0
    DemoWindowWidgetsCollapsingHeaders();
4256
0
    DemoWindowWidgetsComboBoxes();
4257
0
    DemoWindowWidgetsColorAndPickers();
4258
0
    DemoWindowWidgetsDataTypes();
4259
4260
0
    if (disable_all)
4261
0
        ImGui::EndDisabled();
4262
0
    DemoWindowWidgetsDisableBlocks(demo_data);
4263
0
    if (disable_all)
4264
0
        ImGui::BeginDisabled();
4265
4266
0
    DemoWindowWidgetsDragAndDrop();
4267
0
    DemoWindowWidgetsDragsAndSliders();
4268
0
    DemoWindowWidgetsFonts();
4269
0
    DemoWindowWidgetsImages();
4270
0
    DemoWindowWidgetsListBoxes();
4271
0
    DemoWindowWidgetsMultiComponents();
4272
0
    DemoWindowWidgetsPlotting();
4273
0
    DemoWindowWidgetsProgressBars();
4274
0
    DemoWindowWidgetsQueryingStatuses();
4275
0
    DemoWindowWidgetsSelectables();
4276
0
    DemoWindowWidgetsSelectionAndMultiSelect(demo_data);
4277
0
    DemoWindowWidgetsTabs();
4278
0
    DemoWindowWidgetsText();
4279
0
    DemoWindowWidgetsTextFilter();
4280
0
    DemoWindowWidgetsTextInput();
4281
0
    DemoWindowWidgetsTooltips();
4282
0
    DemoWindowWidgetsTreeNodes();
4283
0
    DemoWindowWidgetsVerticalSliders();
4284
4285
0
    if (disable_all)
4286
0
        ImGui::EndDisabled();
4287
0
}
4288
4289
//-----------------------------------------------------------------------------
4290
// [SECTION] DemoWindowLayout()
4291
//-----------------------------------------------------------------------------
4292
4293
static void DemoWindowLayout()
4294
0
{
4295
0
    IMGUI_DEMO_MARKER("Layout");
4296
0
    if (!ImGui::CollapsingHeader("Layout & Scrolling"))
4297
0
        return;
4298
4299
0
    IMGUI_DEMO_MARKER("Layout/Child windows");
4300
0
    if (ImGui::TreeNode("Child windows"))
4301
0
    {
4302
0
        ImGui::SeparatorText("Child windows");
4303
4304
0
        HelpMarker("Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.");
4305
0
        static bool disable_mouse_wheel = false;
4306
0
        static bool disable_menu = false;
4307
0
        ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel);
4308
0
        ImGui::Checkbox("Disable Menu", &disable_menu);
4309
4310
        // Child 1: no border, enable horizontal scrollbar
4311
0
        {
4312
0
            ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;
4313
0
            if (disable_mouse_wheel)
4314
0
                window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4315
0
            ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags);
4316
0
            for (int i = 0; i < 100; i++)
4317
0
                ImGui::Text("%04d: scrollable region", i);
4318
0
            ImGui::EndChild();
4319
0
        }
4320
4321
0
        ImGui::SameLine();
4322
4323
        // Child 2: rounded border
4324
0
        {
4325
0
            ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;
4326
0
            if (disable_mouse_wheel)
4327
0
                window_flags |= ImGuiWindowFlags_NoScrollWithMouse;
4328
0
            if (!disable_menu)
4329
0
                window_flags |= ImGuiWindowFlags_MenuBar;
4330
0
            ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
4331
0
            ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Borders, window_flags);
4332
0
            if (!disable_menu && ImGui::BeginMenuBar())
4333
0
            {
4334
0
                if (ImGui::BeginMenu("Menu"))
4335
0
                {
4336
0
                    ShowExampleMenuFile();
4337
0
                    ImGui::EndMenu();
4338
0
                }
4339
0
                ImGui::EndMenuBar();
4340
0
            }
4341
0
            if (ImGui::BeginTable("split", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))
4342
0
            {
4343
0
                for (int i = 0; i < 100; i++)
4344
0
                {
4345
0
                    char buf[32];
4346
0
                    sprintf(buf, "%03d", i);
4347
0
                    ImGui::TableNextColumn();
4348
0
                    ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
4349
0
                }
4350
0
                ImGui::EndTable();
4351
0
            }
4352
0
            ImGui::EndChild();
4353
0
            ImGui::PopStyleVar();
4354
0
        }
4355
4356
        // Child 3: manual-resize
4357
0
        ImGui::SeparatorText("Manual-resize");
4358
0
        {
4359
0
            HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.");
4360
            //if (ImGui::Button("Set Height to 200"))
4361
            //    ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f));
4362
4363
0
            ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
4364
0
            if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY))
4365
0
                for (int n = 0; n < 10; n++)
4366
0
                    ImGui::Text("Line %04d", n);
4367
0
            ImGui::PopStyleColor();
4368
0
            ImGui::EndChild();
4369
0
        }
4370
4371
        // Child 4: auto-resizing height with a limit
4372
0
        ImGui::SeparatorText("Auto-resize with constraints");
4373
0
        {
4374
0
            static int draw_lines = 3;
4375
0
            static int max_height_in_lines = 10;
4376
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4377
0
            ImGui::DragInt("Lines Count", &draw_lines, 0.2f);
4378
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4379
0
            ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f);
4380
4381
0
            ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));
4382
0
            if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY))
4383
0
                for (int n = 0; n < draw_lines; n++)
4384
0
                    ImGui::Text("Line %04d", n);
4385
0
            ImGui::EndChild();
4386
0
        }
4387
4388
0
        ImGui::SeparatorText("Misc/Advanced");
4389
4390
        // Demonstrate a few extra things
4391
        // - Changing ImGuiCol_ChildBg (which is transparent black in default styles)
4392
        // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)
4393
        //   You can also call SetNextWindowPos() to position the child window. The parent window will effectively
4394
        //   layout from this position.
4395
        // - Using ImGui::GetItemRectMin/Max() to query the "item" state (because the child window is an item from
4396
        //   the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.
4397
0
        {
4398
0
            static int offset_x = 0;
4399
0
            static bool override_bg_color = true;
4400
0
            static ImGuiChildFlags child_flags = ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;
4401
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
4402
0
            ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000);
4403
0
            ImGui::Checkbox("Override ChildBg color", &override_bg_color);
4404
0
            ImGui::CheckboxFlags("ImGuiChildFlags_Borders", &child_flags, ImGuiChildFlags_Borders);
4405
0
            ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding);
4406
0
            ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX);
4407
0
            ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY);
4408
0
            ImGui::CheckboxFlags("ImGuiChildFlags_FrameStyle", &child_flags, ImGuiChildFlags_FrameStyle);
4409
0
            ImGui::SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.");
4410
0
            if (child_flags & ImGuiChildFlags_FrameStyle)
4411
0
                override_bg_color = false;
4412
4413
0
            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);
4414
0
            if (override_bg_color)
4415
0
                ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));
4416
0
            ImGui::BeginChild("Red", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None);
4417
0
            if (override_bg_color)
4418
0
                ImGui::PopStyleColor();
4419
4420
0
            for (int n = 0; n < 50; n++)
4421
0
                ImGui::Text("Some test %d", n);
4422
0
            ImGui::EndChild();
4423
0
            bool child_is_hovered = ImGui::IsItemHovered();
4424
0
            ImVec2 child_rect_min = ImGui::GetItemRectMin();
4425
0
            ImVec2 child_rect_max = ImGui::GetItemRectMax();
4426
0
            ImGui::Text("Hovered: %d", child_is_hovered);
4427
0
            ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y);
4428
0
        }
4429
4430
0
        ImGui::TreePop();
4431
0
    }
4432
4433
0
    IMGUI_DEMO_MARKER("Layout/Widgets Width");
4434
0
    if (ImGui::TreeNode("Widgets Width"))
4435
0
    {
4436
0
        static float f = 0.0f;
4437
0
        static bool show_indented_items = true;
4438
0
        ImGui::Checkbox("Show indented items", &show_indented_items);
4439
4440
        // Use SetNextItemWidth() to set the width of a single upcoming item.
4441
        // Use PushItemWidth()/PopItemWidth() to set the width of a group of items.
4442
        // In real code use you'll probably want to choose width values that are proportional to your font size
4443
        // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.
4444
4445
0
        ImGui::Text("SetNextItemWidth/PushItemWidth(100)");
4446
0
        ImGui::SameLine(); HelpMarker("Fixed width.");
4447
0
        ImGui::PushItemWidth(100);
4448
0
        ImGui::DragFloat("float##1b", &f);
4449
0
        if (show_indented_items)
4450
0
        {
4451
0
            ImGui::Indent();
4452
0
            ImGui::DragFloat("float (indented)##1b", &f);
4453
0
            ImGui::Unindent();
4454
0
        }
4455
0
        ImGui::PopItemWidth();
4456
4457
0
        ImGui::Text("SetNextItemWidth/PushItemWidth(-100)");
4458
0
        ImGui::SameLine(); HelpMarker("Align to right edge minus 100");
4459
0
        ImGui::PushItemWidth(-100);
4460
0
        ImGui::DragFloat("float##2a", &f);
4461
0
        if (show_indented_items)
4462
0
        {
4463
0
            ImGui::Indent();
4464
0
            ImGui::DragFloat("float (indented)##2b", &f);
4465
0
            ImGui::Unindent();
4466
0
        }
4467
0
        ImGui::PopItemWidth();
4468
4469
0
        ImGui::Text("SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)");
4470
0
        ImGui::SameLine(); HelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)");
4471
0
        ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);
4472
0
        ImGui::DragFloat("float##3a", &f);
4473
0
        if (show_indented_items)
4474
0
        {
4475
0
            ImGui::Indent();
4476
0
            ImGui::DragFloat("float (indented)##3b", &f);
4477
0
            ImGui::Unindent();
4478
0
        }
4479
0
        ImGui::PopItemWidth();
4480
4481
0
        ImGui::Text("SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)");
4482
0
        ImGui::SameLine(); HelpMarker("Align to right edge minus half");
4483
0
        ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
4484
0
        ImGui::DragFloat("float##4a", &f);
4485
0
        if (show_indented_items)
4486
0
        {
4487
0
            ImGui::Indent();
4488
0
            ImGui::DragFloat("float (indented)##4b", &f);
4489
0
            ImGui::Unindent();
4490
0
        }
4491
0
        ImGui::PopItemWidth();
4492
4493
0
        ImGui::Text("SetNextItemWidth/PushItemWidth(-Min(GetContentRegionAvail().x * 0.40f, GetFontSize() * 12))");
4494
0
        ImGui::PushItemWidth(-IM_MIN(ImGui::GetFontSize() * 12, ImGui::GetContentRegionAvail().x * 0.40f));
4495
0
        ImGui::DragFloat("float##5a", &f);
4496
0
        if (show_indented_items)
4497
0
        {
4498
0
            ImGui::Indent();
4499
0
            ImGui::DragFloat("float (indented)##5b", &f);
4500
0
            ImGui::Unindent();
4501
0
        }
4502
0
        ImGui::PopItemWidth();
4503
4504
        // Demonstrate using PushItemWidth to surround three items.
4505
        // Calling SetNextItemWidth() before each of them would have the same effect.
4506
0
        ImGui::Text("SetNextItemWidth/PushItemWidth(-FLT_MIN)");
4507
0
        ImGui::SameLine(); HelpMarker("Align to right edge");
4508
0
        ImGui::PushItemWidth(-FLT_MIN);
4509
0
        ImGui::DragFloat("##float6a", &f);
4510
0
        if (show_indented_items)
4511
0
        {
4512
0
            ImGui::Indent();
4513
0
            ImGui::DragFloat("float (indented)##6b", &f);
4514
0
            ImGui::Unindent();
4515
0
        }
4516
0
        ImGui::PopItemWidth();
4517
4518
0
        ImGui::TreePop();
4519
0
    }
4520
4521
0
    IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout");
4522
0
    if (ImGui::TreeNode("Basic Horizontal Layout"))
4523
0
    {
4524
0
        ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)");
4525
4526
        // Text
4527
0
        IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine");
4528
0
        ImGui::Text("Two items: Hello"); ImGui::SameLine();
4529
0
        ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
4530
4531
        // Adjust spacing
4532
0
        ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20);
4533
0
        ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor");
4534
4535
        // Button
4536
0
        ImGui::AlignTextToFramePadding();
4537
0
        ImGui::Text("Normal buttons"); ImGui::SameLine();
4538
0
        ImGui::Button("Banana"); ImGui::SameLine();
4539
0
        ImGui::Button("Apple"); ImGui::SameLine();
4540
0
        ImGui::Button("Corniflower");
4541
4542
        // Button
4543
0
        ImGui::Text("Small buttons"); ImGui::SameLine();
4544
0
        ImGui::SmallButton("Like this one"); ImGui::SameLine();
4545
0
        ImGui::Text("can fit within a text block.");
4546
4547
        // Aligned to arbitrary position. Easy/cheap column.
4548
0
        IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (with offset)");
4549
0
        ImGui::Text("Aligned");
4550
0
        ImGui::SameLine(150); ImGui::Text("x=150");
4551
0
        ImGui::SameLine(300); ImGui::Text("x=300");
4552
0
        ImGui::Text("Aligned");
4553
0
        ImGui::SameLine(150); ImGui::SmallButton("x=150");
4554
0
        ImGui::SameLine(300); ImGui::SmallButton("x=300");
4555
4556
        // Checkbox
4557
0
        IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine (more)");
4558
0
        static bool c1 = false, c2 = false, c3 = false, c4 = false;
4559
0
        ImGui::Checkbox("My", &c1); ImGui::SameLine();
4560
0
        ImGui::Checkbox("Tailor", &c2); ImGui::SameLine();
4561
0
        ImGui::Checkbox("Is", &c3); ImGui::SameLine();
4562
0
        ImGui::Checkbox("Rich", &c4);
4563
4564
        // Various
4565
0
        static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;
4566
0
        ImGui::PushItemWidth(80);
4567
0
        const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" };
4568
0
        static int item = -1;
4569
0
        ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine();
4570
0
        ImGui::SliderFloat("X", &f0, 0.0f, 5.0f); ImGui::SameLine();
4571
0
        ImGui::SliderFloat("Y", &f1, 0.0f, 5.0f); ImGui::SameLine();
4572
0
        ImGui::SliderFloat("Z", &f2, 0.0f, 5.0f);
4573
0
        ImGui::PopItemWidth();
4574
4575
0
        ImGui::PushItemWidth(80);
4576
0
        ImGui::Text("Lists:");
4577
0
        static int selection[4] = { 0, 1, 2, 3 };
4578
0
        for (int i = 0; i < 4; i++)
4579
0
        {
4580
0
            if (i > 0) ImGui::SameLine();
4581
0
            ImGui::PushID(i);
4582
0
            ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items));
4583
0
            ImGui::PopID();
4584
            //ImGui::SetItemTooltip("ListBox %d hovered", i);
4585
0
        }
4586
0
        ImGui::PopItemWidth();
4587
4588
        // Dummy
4589
0
        IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Dummy");
4590
0
        ImVec2 button_sz(40, 40);
4591
0
        ImGui::Button("A", button_sz); ImGui::SameLine();
4592
0
        ImGui::Dummy(button_sz); ImGui::SameLine();
4593
0
        ImGui::Button("B", button_sz);
4594
4595
        // Manually wrapping
4596
        // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)
4597
0
        IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/Manual wrapping");
4598
0
        ImGui::Text("Manual wrapping:");
4599
0
        ImGuiStyle& style = ImGui::GetStyle();
4600
0
        int buttons_count = 20;
4601
0
        float window_visible_x2 = ImGui::GetCursorScreenPos().x + ImGui::GetContentRegionAvail().x;
4602
0
        for (int n = 0; n < buttons_count; n++)
4603
0
        {
4604
0
            ImGui::PushID(n);
4605
0
            ImGui::Button("Box", button_sz);
4606
0
            float last_button_x2 = ImGui::GetItemRectMax().x;
4607
0
            float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line
4608
0
            if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)
4609
0
                ImGui::SameLine();
4610
0
            ImGui::PopID();
4611
0
        }
4612
4613
0
        ImGui::TreePop();
4614
0
    }
4615
4616
0
    IMGUI_DEMO_MARKER("Layout/Groups");
4617
0
    if (ImGui::TreeNode("Groups"))
4618
0
    {
4619
0
        HelpMarker(
4620
0
            "BeginGroup() basically locks the horizontal position for new line. "
4621
0
            "EndGroup() bundles the whole group so that you can use \"item\" functions such as "
4622
0
            "IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.");
4623
0
        ImGui::BeginGroup();
4624
0
        {
4625
0
            ImGui::BeginGroup();
4626
0
            ImGui::Button("AAA");
4627
0
            ImGui::SameLine();
4628
0
            ImGui::Button("BBB");
4629
0
            ImGui::SameLine();
4630
0
            ImGui::BeginGroup();
4631
0
            ImGui::Button("CCC");
4632
0
            ImGui::Button("DDD");
4633
0
            ImGui::EndGroup();
4634
0
            ImGui::SameLine();
4635
0
            ImGui::Button("EEE");
4636
0
            ImGui::EndGroup();
4637
0
            ImGui::SetItemTooltip("First group hovered");
4638
0
        }
4639
        // Capture the group size and create widgets using the same size
4640
0
        ImVec2 size = ImGui::GetItemRectSize();
4641
0
        const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };
4642
0
        ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size);
4643
4644
0
        ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4645
0
        ImGui::SameLine();
4646
0
        ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));
4647
0
        ImGui::EndGroup();
4648
0
        ImGui::SameLine();
4649
4650
0
        ImGui::Button("LEVERAGE\nBUZZWORD", size);
4651
0
        ImGui::SameLine();
4652
4653
0
        if (ImGui::BeginListBox("List", size))
4654
0
        {
4655
0
            ImGui::Selectable("Selected", true);
4656
0
            ImGui::Selectable("Not Selected", false);
4657
0
            ImGui::EndListBox();
4658
0
        }
4659
4660
0
        ImGui::TreePop();
4661
0
    }
4662
4663
0
    IMGUI_DEMO_MARKER("Layout/Text Baseline Alignment");
4664
0
    if (ImGui::TreeNode("Text Baseline Alignment"))
4665
0
    {
4666
0
        {
4667
0
            ImGui::BulletText("Text baseline:");
4668
0
            ImGui::SameLine(); HelpMarker(
4669
0
                "This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. "
4670
0
                "Lines only composed of text or \"small\" widgets use less vertical space than lines with framed widgets.");
4671
0
            ImGui::Indent();
4672
4673
0
            ImGui::Text("KO Blahblah"); ImGui::SameLine();
4674
0
            ImGui::Button("Some framed item"); ImGui::SameLine();
4675
0
            HelpMarker("Baseline of button will look misaligned with text..");
4676
4677
            // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4678
            // (because we don't know what's coming after the Text() statement, we need to move the text baseline
4679
            // down by FramePadding.y ahead of time)
4680
0
            ImGui::AlignTextToFramePadding();
4681
0
            ImGui::Text("OK Blahblah"); ImGui::SameLine();
4682
0
            ImGui::Button("Some framed item##2"); ImGui::SameLine();
4683
0
            HelpMarker("We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y");
4684
4685
            // SmallButton() uses the same vertical padding as Text
4686
0
            ImGui::Button("TEST##1"); ImGui::SameLine();
4687
0
            ImGui::Text("TEST"); ImGui::SameLine();
4688
0
            ImGui::SmallButton("TEST##2");
4689
4690
            // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.
4691
0
            ImGui::AlignTextToFramePadding();
4692
0
            ImGui::Text("Text aligned to framed item"); ImGui::SameLine();
4693
0
            ImGui::Button("Item##1"); ImGui::SameLine();
4694
0
            ImGui::Text("Item"); ImGui::SameLine();
4695
0
            ImGui::SmallButton("Item##2"); ImGui::SameLine();
4696
0
            ImGui::Button("Item##3");
4697
4698
0
            ImGui::Unindent();
4699
0
        }
4700
4701
0
        ImGui::Spacing();
4702
4703
0
        {
4704
0
            ImGui::BulletText("Multi-line text:");
4705
0
            ImGui::Indent();
4706
0
            ImGui::Text("One\nTwo\nThree"); ImGui::SameLine();
4707
0
            ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4708
0
            ImGui::Text("Banana");
4709
4710
0
            ImGui::Text("Banana"); ImGui::SameLine();
4711
0
            ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4712
0
            ImGui::Text("One\nTwo\nThree");
4713
4714
0
            ImGui::Button("HOP##1"); ImGui::SameLine();
4715
0
            ImGui::Text("Banana"); ImGui::SameLine();
4716
0
            ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4717
0
            ImGui::Text("Banana");
4718
4719
0
            ImGui::Button("HOP##2"); ImGui::SameLine();
4720
0
            ImGui::Text("Hello\nWorld"); ImGui::SameLine();
4721
0
            ImGui::Text("Banana");
4722
0
            ImGui::Unindent();
4723
0
        }
4724
4725
0
        ImGui::Spacing();
4726
4727
0
        {
4728
0
            ImGui::BulletText("Misc items:");
4729
0
            ImGui::Indent();
4730
4731
            // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.
4732
0
            ImGui::Button("80x80", ImVec2(80, 80));
4733
0
            ImGui::SameLine();
4734
0
            ImGui::Button("50x50", ImVec2(50, 50));
4735
0
            ImGui::SameLine();
4736
0
            ImGui::Button("Button()");
4737
0
            ImGui::SameLine();
4738
0
            ImGui::SmallButton("SmallButton()");
4739
4740
            // Tree
4741
            // (here the node appears after a button and has odd intent, so we use ImGuiTreeNodeFlags_DrawLinesNone to disable hierarchy outline)
4742
0
            const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;
4743
0
            ImGui::Button("Button##1");
4744
0
            ImGui::SameLine(0.0f, spacing);
4745
0
            if (ImGui::TreeNodeEx("Node##1", ImGuiTreeNodeFlags_DrawLinesNone))
4746
0
            {
4747
                // Placeholder tree data
4748
0
                for (int i = 0; i < 6; i++)
4749
0
                    ImGui::BulletText("Item %d..", i);
4750
0
                ImGui::TreePop();
4751
0
            }
4752
4753
            // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.
4754
            // Otherwise you can use SmallButton() (smaller fit).
4755
0
            ImGui::AlignTextToFramePadding();
4756
4757
            // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add
4758
            // other contents below the node.
4759
0
            bool node_open = ImGui::TreeNode("Node##2");
4760
0
            ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2");
4761
0
            if (node_open)
4762
0
            {
4763
                // Placeholder tree data
4764
0
                for (int i = 0; i < 6; i++)
4765
0
                    ImGui::BulletText("Item %d..", i);
4766
0
                ImGui::TreePop();
4767
0
            }
4768
4769
            // Bullet
4770
0
            ImGui::Button("Button##3");
4771
0
            ImGui::SameLine(0.0f, spacing);
4772
0
            ImGui::BulletText("Bullet text");
4773
4774
0
            ImGui::AlignTextToFramePadding();
4775
0
            ImGui::BulletText("Node");
4776
0
            ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4");
4777
0
            ImGui::Unindent();
4778
0
        }
4779
4780
0
        ImGui::TreePop();
4781
0
    }
4782
4783
0
    IMGUI_DEMO_MARKER("Layout/Scrolling");
4784
0
    if (ImGui::TreeNode("Scrolling"))
4785
0
    {
4786
        // Vertical scroll functions
4787
0
        IMGUI_DEMO_MARKER("Layout/Scrolling/Vertical");
4788
0
        HelpMarker("Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.");
4789
4790
0
        static int track_item = 50;
4791
0
        static bool enable_track = true;
4792
0
        static bool enable_extra_decorations = false;
4793
0
        static float scroll_to_off_px = 0.0f;
4794
0
        static float scroll_to_pos_px = 200.0f;
4795
4796
0
        ImGui::Checkbox("Decoration", &enable_extra_decorations);
4797
4798
0
        ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
4799
0
        enable_track |= ImGui::DragInt("##item", &track_item, 0.25f, 0, 99, "Item = %d");
4800
0
        ImGui::SameLine();
4801
0
        ImGui::Checkbox("Track", &enable_track);
4802
4803
0
        bool scroll_to_off = ImGui::DragFloat("##off", &scroll_to_off_px, 1.00f, 0, FLT_MAX, "+%.0f px");
4804
0
        ImGui::SameLine();
4805
0
        scroll_to_off |= ImGui::Button("Scroll Offset");
4806
4807
0
        bool scroll_to_pos = ImGui::DragFloat("##pos", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, "X/Y = %.0f px");
4808
0
        ImGui::SameLine();
4809
0
        scroll_to_pos |= ImGui::Button("Scroll To Pos");
4810
0
        ImGui::PopItemWidth();
4811
4812
0
        if (scroll_to_off || scroll_to_pos)
4813
0
            enable_track = false;
4814
4815
0
        ImGuiStyle& style = ImGui::GetStyle();
4816
0
        float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;
4817
0
        if (child_w < 1.0f)
4818
0
            child_w = 1.0f;
4819
0
        ImGui::PushID("##VerticalScrolling");
4820
0
        for (int i = 0; i < 5; i++)
4821
0
        {
4822
0
            if (i > 0) ImGui::SameLine();
4823
0
            ImGui::BeginGroup();
4824
0
            const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
4825
0
            ImGui::TextUnformatted(names[i]);
4826
4827
0
            const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
4828
0
            const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
4829
0
            const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Borders, child_flags);
4830
0
            if (ImGui::BeginMenuBar())
4831
0
            {
4832
0
                ImGui::TextUnformatted("abc");
4833
0
                ImGui::EndMenuBar();
4834
0
            }
4835
0
            if (scroll_to_off)
4836
0
                ImGui::SetScrollY(scroll_to_off_px);
4837
0
            if (scroll_to_pos)
4838
0
                ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);
4839
0
            if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4840
0
            {
4841
0
                for (int item = 0; item < 100; item++)
4842
0
                {
4843
0
                    if (enable_track && item == track_item)
4844
0
                    {
4845
0
                        ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
4846
0
                        ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
4847
0
                    }
4848
0
                    else
4849
0
                    {
4850
0
                        ImGui::Text("Item %d", item);
4851
0
                    }
4852
0
                }
4853
0
            }
4854
0
            float scroll_y = ImGui::GetScrollY();
4855
0
            float scroll_max_y = ImGui::GetScrollMaxY();
4856
0
            ImGui::EndChild();
4857
0
            ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y);
4858
0
            ImGui::EndGroup();
4859
0
        }
4860
0
        ImGui::PopID();
4861
4862
        // Horizontal scroll functions
4863
0
        IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal");
4864
0
        ImGui::Spacing();
4865
0
        HelpMarker(
4866
0
            "Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\n\n"
4867
0
            "Because the clipping rectangle of most window hides half worth of WindowPadding on the "
4868
0
            "left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the "
4869
0
            "equivalent SetScrollFromPosY(+1) wouldn't.");
4870
0
        ImGui::PushID("##HorizontalScrolling");
4871
0
        for (int i = 0; i < 5; i++)
4872
0
        {
4873
0
            float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;
4874
0
            ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);
4875
0
            ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
4876
0
            bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Borders, child_flags);
4877
0
            if (scroll_to_off)
4878
0
                ImGui::SetScrollX(scroll_to_off_px);
4879
0
            if (scroll_to_pos)
4880
0
                ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);
4881
0
            if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
4882
0
            {
4883
0
                for (int item = 0; item < 100; item++)
4884
0
                {
4885
0
                    if (item > 0)
4886
0
                        ImGui::SameLine();
4887
0
                    if (enable_track && item == track_item)
4888
0
                    {
4889
0
                        ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
4890
0
                        ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right
4891
0
                    }
4892
0
                    else
4893
0
                    {
4894
0
                        ImGui::Text("Item %d", item);
4895
0
                    }
4896
0
                }
4897
0
            }
4898
0
            float scroll_x = ImGui::GetScrollX();
4899
0
            float scroll_max_x = ImGui::GetScrollMaxX();
4900
0
            ImGui::EndChild();
4901
0
            ImGui::SameLine();
4902
0
            const char* names[] = { "Left", "25%", "Center", "75%", "Right" };
4903
0
            ImGui::Text("%s\n%.0f/%.0f", names[i], scroll_x, scroll_max_x);
4904
0
            ImGui::Spacing();
4905
0
        }
4906
0
        ImGui::PopID();
4907
4908
        // Miscellaneous Horizontal Scrolling Demo
4909
0
        IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal (more)");
4910
0
        HelpMarker(
4911
0
            "Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\n\n"
4912
0
            "You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().");
4913
0
        static int lines = 7;
4914
0
        ImGui::SliderInt("Lines", &lines, 1, 15);
4915
0
        ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
4916
0
        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
4917
0
        ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);
4918
0
        ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);
4919
0
        for (int line = 0; line < lines; line++)
4920
0
        {
4921
            // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()
4922
            // If you want to create your own time line for a real application you may be better off manipulating
4923
            // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets
4924
            // yourself. You may also want to use the lower-level ImDrawList API.
4925
0
            int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);
4926
0
            for (int n = 0; n < num_buttons; n++)
4927
0
            {
4928
0
                if (n > 0) ImGui::SameLine();
4929
0
                ImGui::PushID(n + line * 1000);
4930
0
                char num_buf[16];
4931
0
                sprintf(num_buf, "%d", n);
4932
0
                const char* label = (!(n % 15)) ? "FizzBuzz" : (!(n % 3)) ? "Fizz" : (!(n % 5)) ? "Buzz" : num_buf;
4933
0
                float hue = n * 0.05f;
4934
0
                ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));
4935
0
                ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));
4936
0
                ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));
4937
0
                ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f));
4938
0
                ImGui::PopStyleColor(3);
4939
0
                ImGui::PopID();
4940
0
            }
4941
0
        }
4942
0
        float scroll_x = ImGui::GetScrollX();
4943
0
        float scroll_max_x = ImGui::GetScrollMaxX();
4944
0
        ImGui::EndChild();
4945
0
        ImGui::PopStyleVar(2);
4946
0
        float scroll_x_delta = 0.0f;
4947
0
        ImGui::SmallButton("<<");
4948
0
        if (ImGui::IsItemActive())
4949
0
            scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;
4950
0
        ImGui::SameLine();
4951
0
        ImGui::Text("Scroll from code"); ImGui::SameLine();
4952
0
        ImGui::SmallButton(">>");
4953
0
        if (ImGui::IsItemActive())
4954
0
            scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;
4955
0
        ImGui::SameLine();
4956
0
        ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x);
4957
0
        if (scroll_x_delta != 0.0f)
4958
0
        {
4959
            // Demonstrate a trick: you can use Begin to set yourself in the context of another window
4960
            // (here we are already out of your child window)
4961
0
            ImGui::BeginChild("scrolling");
4962
0
            ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);
4963
0
            ImGui::EndChild();
4964
0
        }
4965
0
        ImGui::Spacing();
4966
4967
0
        static bool show_horizontal_contents_size_demo_window = false;
4968
0
        ImGui::Checkbox("Show Horizontal contents size demo window", &show_horizontal_contents_size_demo_window);
4969
4970
0
        if (show_horizontal_contents_size_demo_window)
4971
0
        {
4972
0
            static bool show_h_scrollbar = true;
4973
0
            static bool show_button = true;
4974
0
            static bool show_tree_nodes = true;
4975
0
            static bool show_text_wrapped = false;
4976
0
            static bool show_columns = true;
4977
0
            static bool show_tab_bar = true;
4978
0
            static bool show_child = false;
4979
0
            static bool explicit_content_size = false;
4980
0
            static float contents_size_x = 300.0f;
4981
0
            if (explicit_content_size)
4982
0
                ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));
4983
0
            ImGui::Begin("Horizontal contents size demo window", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);
4984
0
            IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window");
4985
0
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));
4986
0
            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));
4987
0
            HelpMarker(
4988
0
                "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n"
4989
0
                "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles.");
4990
0
            ImGui::Checkbox("H-scrollbar", &show_h_scrollbar);
4991
0
            ImGui::Checkbox("Button", &show_button);            // Will grow contents size (unless explicitly overwritten)
4992
0
            ImGui::Checkbox("Tree nodes", &show_tree_nodes);    // Will grow contents size and display highlight over full width
4993
0
            ImGui::Checkbox("Text wrapped", &show_text_wrapped);// Will grow and use contents size
4994
0
            ImGui::Checkbox("Columns", &show_columns);          // Will use contents size
4995
0
            ImGui::Checkbox("Tab bar", &show_tab_bar);          // Will use contents size
4996
0
            ImGui::Checkbox("Child", &show_child);              // Will grow and use contents size
4997
0
            ImGui::Checkbox("Explicit content size", &explicit_content_size);
4998
0
            ImGui::Text("Scroll %.1f/%.1f %.1f/%.1f", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());
4999
0
            if (explicit_content_size)
5000
0
            {
5001
0
                ImGui::SameLine();
5002
0
                ImGui::SetNextItemWidth(100);
5003
0
                ImGui::DragFloat("##csx", &contents_size_x);
5004
0
                ImVec2 p = ImGui::GetCursorScreenPos();
5005
0
                ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);
5006
0
                ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);
5007
0
                ImGui::Dummy(ImVec2(0, 10));
5008
0
            }
5009
0
            ImGui::PopStyleVar(2);
5010
0
            ImGui::Separator();
5011
0
            if (show_button)
5012
0
            {
5013
0
                ImGui::Button("this is a 300-wide button", ImVec2(300, 0));
5014
0
            }
5015
0
            if (show_tree_nodes)
5016
0
            {
5017
0
                bool open = true;
5018
0
                if (ImGui::TreeNode("this is a tree node"))
5019
0
                {
5020
0
                    if (ImGui::TreeNode("another one of those tree node..."))
5021
0
                    {
5022
0
                        ImGui::Text("Some tree contents");
5023
0
                        ImGui::TreePop();
5024
0
                    }
5025
0
                    ImGui::TreePop();
5026
0
                }
5027
0
                ImGui::CollapsingHeader("CollapsingHeader", &open);
5028
0
            }
5029
0
            if (show_text_wrapped)
5030
0
            {
5031
0
                ImGui::TextWrapped("This text should automatically wrap on the edge of the work rectangle.");
5032
0
            }
5033
0
            if (show_columns)
5034
0
            {
5035
0
                ImGui::Text("Tables:");
5036
0
                if (ImGui::BeginTable("table", 4, ImGuiTableFlags_Borders))
5037
0
                {
5038
0
                    for (int n = 0; n < 4; n++)
5039
0
                    {
5040
0
                        ImGui::TableNextColumn();
5041
0
                        ImGui::Text("Width %.2f", ImGui::GetContentRegionAvail().x);
5042
0
                    }
5043
0
                    ImGui::EndTable();
5044
0
                }
5045
0
                ImGui::Text("Columns:");
5046
0
                ImGui::Columns(4);
5047
0
                for (int n = 0; n < 4; n++)
5048
0
                {
5049
0
                    ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
5050
0
                    ImGui::NextColumn();
5051
0
                }
5052
0
                ImGui::Columns(1);
5053
0
            }
5054
0
            if (show_tab_bar && ImGui::BeginTabBar("Hello"))
5055
0
            {
5056
0
                if (ImGui::BeginTabItem("OneOneOne")) { ImGui::EndTabItem(); }
5057
0
                if (ImGui::BeginTabItem("TwoTwoTwo")) { ImGui::EndTabItem(); }
5058
0
                if (ImGui::BeginTabItem("ThreeThreeThree")) { ImGui::EndTabItem(); }
5059
0
                if (ImGui::BeginTabItem("FourFourFour")) { ImGui::EndTabItem(); }
5060
0
                ImGui::EndTabBar();
5061
0
            }
5062
0
            if (show_child)
5063
0
            {
5064
0
                ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Borders);
5065
0
                ImGui::EndChild();
5066
0
            }
5067
0
            ImGui::End();
5068
0
        }
5069
5070
0
        ImGui::TreePop();
5071
0
    }
5072
5073
0
    IMGUI_DEMO_MARKER("Layout/Text Clipping");
5074
0
    if (ImGui::TreeNode("Text Clipping"))
5075
0
    {
5076
0
        static ImVec2 size(100.0f, 100.0f);
5077
0
        static ImVec2 offset(30.0f, 30.0f);
5078
0
        ImGui::DragFloat2("size", (float*)&size, 0.5f, 1.0f, 200.0f, "%.0f");
5079
0
        ImGui::TextWrapped("(Click and drag to scroll)");
5080
5081
0
        HelpMarker(
5082
0
            "(Left) Using ImGui::PushClipRect():\n"
5083
0
            "Will alter ImGui hit-testing logic + ImDrawList rendering.\n"
5084
0
            "(use this if you want your clipping rectangle to affect interactions)\n\n"
5085
0
            "(Center) Using ImDrawList::PushClipRect():\n"
5086
0
            "Will alter ImDrawList rendering only.\n"
5087
0
            "(use this as a shortcut if you are only using ImDrawList calls)\n\n"
5088
0
            "(Right) Using ImDrawList::AddText() with a fine ClipRect:\n"
5089
0
            "Will alter only this specific ImDrawList::AddText() rendering.\n"
5090
0
            "This is often used internally to avoid altering the clipping rectangle and minimize draw calls.");
5091
5092
0
        for (int n = 0; n < 3; n++)
5093
0
        {
5094
0
            if (n > 0)
5095
0
                ImGui::SameLine();
5096
5097
0
            ImGui::PushID(n);
5098
0
            ImGui::InvisibleButton("##canvas", size);
5099
0
            if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
5100
0
            {
5101
0
                offset.x += ImGui::GetIO().MouseDelta.x;
5102
0
                offset.y += ImGui::GetIO().MouseDelta.y;
5103
0
            }
5104
0
            ImGui::PopID();
5105
0
            if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.
5106
0
                continue;
5107
5108
0
            const ImVec2 p0 = ImGui::GetItemRectMin();
5109
0
            const ImVec2 p1 = ImGui::GetItemRectMax();
5110
0
            const char* text_str = "Line 1 hello\nLine 2 clip me!";
5111
0
            const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);
5112
0
            ImDrawList* draw_list = ImGui::GetWindowDrawList();
5113
0
            switch (n)
5114
0
            {
5115
0
            case 0:
5116
0
                ImGui::PushClipRect(p0, p1, true);
5117
0
                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5118
0
                draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
5119
0
                ImGui::PopClipRect();
5120
0
                break;
5121
0
            case 1:
5122
0
                draw_list->PushClipRect(p0, p1, true);
5123
0
                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5124
0
                draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);
5125
0
                draw_list->PopClipRect();
5126
0
                break;
5127
0
            case 2:
5128
0
                ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.
5129
0
                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));
5130
0
                draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);
5131
0
                break;
5132
0
            }
5133
0
        }
5134
5135
0
        ImGui::TreePop();
5136
0
    }
5137
5138
0
    IMGUI_DEMO_MARKER("Layout/Overlap Mode");
5139
0
    if (ImGui::TreeNode("Overlap Mode"))
5140
0
    {
5141
0
        static bool enable_allow_overlap = true;
5142
5143
0
        HelpMarker(
5144
0
            "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n"
5145
0
            "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. "
5146
0
            "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.");
5147
0
        ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap);
5148
5149
0
        ImVec2 button1_pos = ImGui::GetCursorScreenPos();
5150
0
        ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);
5151
0
        if (enable_allow_overlap)
5152
0
            ImGui::SetNextItemAllowOverlap();
5153
0
        ImGui::Button("Button 1", ImVec2(80, 80));
5154
0
        ImGui::SetCursorScreenPos(button2_pos);
5155
0
        ImGui::Button("Button 2", ImVec2(80, 80));
5156
5157
        // This is typically used with width-spanning items.
5158
        // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut
5159
        // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)
5160
0
        if (enable_allow_overlap)
5161
0
            ImGui::SetNextItemAllowOverlap();
5162
0
        ImGui::Selectable("Some Selectable", false);
5163
0
        ImGui::SameLine();
5164
0
        ImGui::SmallButton("++");
5165
5166
0
        ImGui::TreePop();
5167
0
    }
5168
0
}
5169
5170
//-----------------------------------------------------------------------------
5171
// [SECTION] DemoWindowPopups()
5172
//-----------------------------------------------------------------------------
5173
5174
static void DemoWindowPopups()
5175
0
{
5176
0
    IMGUI_DEMO_MARKER("Popups");
5177
0
    if (!ImGui::CollapsingHeader("Popups & Modal windows"))
5178
0
        return;
5179
5180
    // The properties of popups windows are:
5181
    // - They block normal mouse hovering detection outside them. (*)
5182
    // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.
5183
    // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as
5184
    //   we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().
5185
    // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even
5186
    //     when normally blocked by a popup.
5187
    // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close
5188
    // popups at any time.
5189
5190
    // Typical use for regular windows:
5191
    //   bool my_tool_is_active = false; if (ImGui::Button("Open")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin("My Tool", &my_tool_is_active) { [...] } End();
5192
    // Typical use for popups:
5193
    //   if (ImGui::Button("Open")) ImGui::OpenPopup("MyPopup"); if (ImGui::BeginPopup("MyPopup")) { [...] EndPopup(); }
5194
5195
    // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.
5196
    // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.
5197
5198
0
    IMGUI_DEMO_MARKER("Popups/Popups");
5199
0
    if (ImGui::TreeNode("Popups"))
5200
0
    {
5201
0
        ImGui::TextWrapped(
5202
0
            "When a popup is active, it inhibits interacting with windows that are behind the popup. "
5203
0
            "Clicking outside the popup closes it.");
5204
5205
0
        static int selected_fish = -1;
5206
0
        const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" };
5207
0
        static bool toggles[] = { true, false, false, false, false };
5208
5209
        // Simple selection popup (if you want to show the current selection inside the Button itself,
5210
        // you may want to build a string using the "###" operator to preserve a constant ID with a variable label)
5211
0
        if (ImGui::Button("Select.."))
5212
0
            ImGui::OpenPopup("my_select_popup");
5213
0
        ImGui::SameLine();
5214
0
        ImGui::TextUnformatted(selected_fish == -1 ? "<None>" : names[selected_fish]);
5215
0
        if (ImGui::BeginPopup("my_select_popup"))
5216
0
        {
5217
0
            ImGui::SeparatorText("Aquarium");
5218
0
            for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5219
0
                if (ImGui::Selectable(names[i]))
5220
0
                    selected_fish = i;
5221
0
            ImGui::EndPopup();
5222
0
        }
5223
5224
        // Showing a menu with toggles
5225
0
        if (ImGui::Button("Toggle.."))
5226
0
            ImGui::OpenPopup("my_toggle_popup");
5227
0
        if (ImGui::BeginPopup("my_toggle_popup"))
5228
0
        {
5229
0
            for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5230
0
                ImGui::MenuItem(names[i], "", &toggles[i]);
5231
0
            if (ImGui::BeginMenu("Sub-menu"))
5232
0
            {
5233
0
                ImGui::MenuItem("Click me");
5234
0
                ImGui::EndMenu();
5235
0
            }
5236
5237
0
            ImGui::Separator();
5238
0
            ImGui::Text("Tooltip here");
5239
0
            ImGui::SetItemTooltip("I am a tooltip over a popup");
5240
5241
0
            if (ImGui::Button("Stacked Popup"))
5242
0
                ImGui::OpenPopup("another popup");
5243
0
            if (ImGui::BeginPopup("another popup"))
5244
0
            {
5245
0
                for (int i = 0; i < IM_ARRAYSIZE(names); i++)
5246
0
                    ImGui::MenuItem(names[i], "", &toggles[i]);
5247
0
                if (ImGui::BeginMenu("Sub-menu"))
5248
0
                {
5249
0
                    ImGui::MenuItem("Click me");
5250
0
                    if (ImGui::Button("Stacked Popup"))
5251
0
                        ImGui::OpenPopup("another popup");
5252
0
                    if (ImGui::BeginPopup("another popup"))
5253
0
                    {
5254
0
                        ImGui::Text("I am the last one here.");
5255
0
                        ImGui::EndPopup();
5256
0
                    }
5257
0
                    ImGui::EndMenu();
5258
0
                }
5259
0
                ImGui::EndPopup();
5260
0
            }
5261
0
            ImGui::EndPopup();
5262
0
        }
5263
5264
        // Call the more complete ShowExampleMenuFile which we use in various places of this demo
5265
0
        if (ImGui::Button("With a menu.."))
5266
0
            ImGui::OpenPopup("my_file_popup");
5267
0
        if (ImGui::BeginPopup("my_file_popup", ImGuiWindowFlags_MenuBar))
5268
0
        {
5269
0
            if (ImGui::BeginMenuBar())
5270
0
            {
5271
0
                if (ImGui::BeginMenu("File"))
5272
0
                {
5273
0
                    ShowExampleMenuFile();
5274
0
                    ImGui::EndMenu();
5275
0
                }
5276
0
                if (ImGui::BeginMenu("Edit"))
5277
0
                {
5278
0
                    ImGui::MenuItem("Dummy");
5279
0
                    ImGui::EndMenu();
5280
0
                }
5281
0
                ImGui::EndMenuBar();
5282
0
            }
5283
0
            ImGui::Text("Hello from popup!");
5284
0
            ImGui::Button("This is a dummy button..");
5285
0
            ImGui::EndPopup();
5286
0
        }
5287
5288
0
        ImGui::TreePop();
5289
0
    }
5290
5291
0
    IMGUI_DEMO_MARKER("Popups/Context menus");
5292
0
    if (ImGui::TreeNode("Context menus"))
5293
0
    {
5294
0
        HelpMarker("\"Context\" functions are simple helpers to associate a Popup to a given Item or Window identifier.");
5295
5296
        // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:
5297
        //     if (id == 0)
5298
        //         id = GetItemID(); // Use last item id
5299
        //     if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
5300
        //         OpenPopup(id);
5301
        //     return BeginPopup(id);
5302
        // For advanced uses you may want to replicate and customize this code.
5303
        // See more details in BeginPopupContextItem().
5304
5305
        // Example 1
5306
        // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),
5307
        // and BeginPopupContextItem() will use the last item ID as the popup ID.
5308
0
        {
5309
0
            const char* names[5] = { "Label1", "Label2", "Label3", "Label4", "Label5" };
5310
0
            static int selected = -1;
5311
0
            for (int n = 0; n < 5; n++)
5312
0
            {
5313
0
                if (ImGui::Selectable(names[n], selected == n))
5314
0
                    selected = n;
5315
0
                if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id
5316
0
                {
5317
0
                    selected = n;
5318
0
                    ImGui::Text("This a popup for \"%s\"!", names[n]);
5319
0
                    if (ImGui::Button("Close"))
5320
0
                        ImGui::CloseCurrentPopup();
5321
0
                    ImGui::EndPopup();
5322
0
                }
5323
0
                ImGui::SetItemTooltip("Right-click to open popup");
5324
0
            }
5325
0
        }
5326
5327
        // Example 2
5328
        // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().
5329
        // Using an explicit identifier is also convenient if you want to activate the popups from different locations.
5330
0
        {
5331
0
            HelpMarker("Text() elements don't have stable identifiers so we need to provide one.");
5332
0
            static float value = 0.5f;
5333
0
            ImGui::Text("Value = %.3f <-- (1) right-click this text", value);
5334
0
            if (ImGui::BeginPopupContextItem("my popup"))
5335
0
            {
5336
0
                if (ImGui::Selectable("Set to zero")) value = 0.0f;
5337
0
                if (ImGui::Selectable("Set to PI")) value = 3.1415f;
5338
0
                ImGui::SetNextItemWidth(-FLT_MIN);
5339
0
                ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f);
5340
0
                ImGui::EndPopup();
5341
0
            }
5342
5343
            // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.
5344
            // Here we make it that right-clicking this other text element opens the same popup as above.
5345
            // The popup itself will be submitted by the code above.
5346
0
            ImGui::Text("(2) Or right-click this text");
5347
0
            ImGui::OpenPopupOnItemClick("my popup", ImGuiPopupFlags_MouseButtonRight);
5348
5349
            // Back to square one: manually open the same popup.
5350
0
            if (ImGui::Button("(3) Or click this button"))
5351
0
                ImGui::OpenPopup("my popup");
5352
0
        }
5353
5354
        // Example 3
5355
        // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),
5356
        // we need to make sure your item identifier is stable.
5357
        // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).
5358
0
        {
5359
0
            HelpMarker("Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.");
5360
0
            static char name[32] = "Label1";
5361
0
            char buf[64];
5362
0
            sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label
5363
0
            ImGui::Button(buf);
5364
0
            if (ImGui::BeginPopupContextItem())
5365
0
            {
5366
0
                ImGui::Text("Edit name:");
5367
0
                ImGui::InputText("##edit", name, IM_ARRAYSIZE(name));
5368
0
                if (ImGui::Button("Close"))
5369
0
                    ImGui::CloseCurrentPopup();
5370
0
                ImGui::EndPopup();
5371
0
            }
5372
0
            ImGui::SameLine(); ImGui::Text("(<-- right-click here)");
5373
0
        }
5374
5375
0
        ImGui::TreePop();
5376
0
    }
5377
5378
0
    IMGUI_DEMO_MARKER("Popups/Modals");
5379
0
    if (ImGui::TreeNode("Modals"))
5380
0
    {
5381
0
        ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside.");
5382
5383
0
        if (ImGui::Button("Delete.."))
5384
0
            ImGui::OpenPopup("Delete?");
5385
5386
        // Always center this window when appearing
5387
0
        ImVec2 center = ImGui::GetMainViewport()->GetCenter();
5388
0
        ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
5389
5390
0
        if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
5391
0
        {
5392
0
            ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!");
5393
0
            ImGui::Separator();
5394
5395
            //static int unused_i = 0;
5396
            //ImGui::Combo("Combo", &unused_i, "Delete\0Delete harder\0");
5397
5398
0
            static bool dont_ask_me_next_time = false;
5399
0
            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
5400
0
            ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time);
5401
0
            ImGui::PopStyleVar();
5402
5403
0
            if (ImGui::Button("OK", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5404
0
            ImGui::SetItemDefaultFocus();
5405
0
            ImGui::SameLine();
5406
0
            if (ImGui::Button("Cancel", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }
5407
0
            ImGui::EndPopup();
5408
0
        }
5409
5410
0
        if (ImGui::Button("Stacked modals.."))
5411
0
            ImGui::OpenPopup("Stacked 1");
5412
0
        if (ImGui::BeginPopupModal("Stacked 1", NULL, ImGuiWindowFlags_MenuBar))
5413
0
        {
5414
0
            if (ImGui::BeginMenuBar())
5415
0
            {
5416
0
                if (ImGui::BeginMenu("File"))
5417
0
                {
5418
0
                    if (ImGui::MenuItem("Some menu item")) {}
5419
0
                    ImGui::EndMenu();
5420
0
                }
5421
0
                ImGui::EndMenuBar();
5422
0
            }
5423
0
            ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.");
5424
5425
            // Testing behavior of widgets stacking their own regular popups over the modal.
5426
0
            static int item = 1;
5427
0
            static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };
5428
0
            ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0");
5429
0
            ImGui::ColorEdit4("Color", color);
5430
5431
0
            if (ImGui::Button("Add another modal.."))
5432
0
                ImGui::OpenPopup("Stacked 2");
5433
5434
            // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which
5435
            // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value
5436
            // of the bool actually doesn't matter here.
5437
0
            bool unused_open = true;
5438
0
            if (ImGui::BeginPopupModal("Stacked 2", &unused_open))
5439
0
            {
5440
0
                ImGui::Text("Hello from Stacked The Second!");
5441
0
                ImGui::ColorEdit4("Color", color); // Allow opening another nested popup
5442
0
                if (ImGui::Button("Close"))
5443
0
                    ImGui::CloseCurrentPopup();
5444
0
                ImGui::EndPopup();
5445
0
            }
5446
5447
0
            if (ImGui::Button("Close"))
5448
0
                ImGui::CloseCurrentPopup();
5449
0
            ImGui::EndPopup();
5450
0
        }
5451
5452
0
        ImGui::TreePop();
5453
0
    }
5454
5455
0
    IMGUI_DEMO_MARKER("Popups/Menus inside a regular window");
5456
0
    if (ImGui::TreeNode("Menus inside a regular window"))
5457
0
    {
5458
0
        ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!");
5459
0
        ImGui::Separator();
5460
5461
0
        ImGui::MenuItem("Menu item", "CTRL+M");
5462
0
        if (ImGui::BeginMenu("Menu inside a regular window"))
5463
0
        {
5464
0
            ShowExampleMenuFile();
5465
0
            ImGui::EndMenu();
5466
0
        }
5467
0
        ImGui::Separator();
5468
0
        ImGui::TreePop();
5469
0
    }
5470
0
}
5471
5472
// Dummy data structure that we use for the Table demo.
5473
// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)
5474
namespace
5475
{
5476
// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.
5477
// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.
5478
// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)
5479
// If you don't use sorting, you will generally never care about giving column an ID!
5480
enum MyItemColumnID
5481
{
5482
    MyItemColumnID_ID,
5483
    MyItemColumnID_Name,
5484
    MyItemColumnID_Action,
5485
    MyItemColumnID_Quantity,
5486
    MyItemColumnID_Description
5487
};
5488
5489
struct MyItem
5490
{
5491
    int         ID;
5492
    const char* Name;
5493
    int         Quantity;
5494
5495
    // We have a problem which is affecting _only this demo_ and should not affect your code:
5496
    // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),
5497
    // however qsort doesn't allow passing user data to comparing function.
5498
    // As a workaround, we are storing the sort specs in a static/global for the comparing function to access.
5499
    // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.
5500
    // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called
5501
    // very often by the sorting algorithm it would be a little wasteful.
5502
    static const ImGuiTableSortSpecs* s_current_sort_specs;
5503
5504
    static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)
5505
0
    {
5506
0
        s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
5507
0
        if (items_count > 1)
5508
0
            qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs);
5509
0
        s_current_sort_specs = NULL;
5510
0
    }
5511
5512
    // Compare function to be used by qsort()
5513
    static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
5514
0
    {
5515
0
        const MyItem* a = (const MyItem*)lhs;
5516
0
        const MyItem* b = (const MyItem*)rhs;
5517
0
        for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
5518
0
        {
5519
            // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()
5520
            // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!
5521
0
            const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
5522
0
            int delta = 0;
5523
0
            switch (sort_spec->ColumnUserID)
5524
0
            {
5525
0
            case MyItemColumnID_ID:             delta = (a->ID - b->ID);                break;
5526
0
            case MyItemColumnID_Name:           delta = (strcmp(a->Name, b->Name));     break;
5527
0
            case MyItemColumnID_Quantity:       delta = (a->Quantity - b->Quantity);    break;
5528
0
            case MyItemColumnID_Description:    delta = (strcmp(a->Name, b->Name));     break;
5529
0
            default: IM_ASSERT(0); break;
5530
0
            }
5531
0
            if (delta > 0)
5532
0
                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
5533
0
            if (delta < 0)
5534
0
                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
5535
0
        }
5536
5537
        // qsort() is instable so always return a way to differentiate items.
5538
        // Your own compare function may want to avoid fallback on implicit sort specs.
5539
        // e.g. a Name compare if it wasn't already part of the sort specs.
5540
0
        return (a->ID - b->ID);
5541
0
    }
5542
};
5543
const ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;
5544
}
5545
5546
// Make the UI compact because there are so many fields
5547
static void PushStyleCompact()
5548
0
{
5549
0
    ImGuiStyle& style = ImGui::GetStyle();
5550
0
    ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, (float)(int)(style.FramePadding.y * 0.60f));
5551
0
    ImGui::PushStyleVarY(ImGuiStyleVar_ItemSpacing, (float)(int)(style.ItemSpacing.y * 0.60f));
5552
0
}
5553
5554
static void PopStyleCompact()
5555
0
{
5556
0
    ImGui::PopStyleVar(2);
5557
0
}
5558
5559
// Show a combo box with a choice of sizing policies
5560
static void EditTableSizingFlags(ImGuiTableFlags* p_flags)
5561
0
{
5562
0
    struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };
5563
0
    static const EnumDesc policies[] =
5564
0
    {
5565
0
        { ImGuiTableFlags_None,               "Default",                            "Use default sizing policy:\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n- ImGuiTableFlags_SizingStretchSame otherwise." },
5566
0
        { ImGuiTableFlags_SizingFixedFit,     "ImGuiTableFlags_SizingFixedFit",     "Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width." },
5567
0
        { ImGuiTableFlags_SizingFixedSame,    "ImGuiTableFlags_SizingFixedSame",    "Columns are all the same width, matching the maximum contents width.\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible." },
5568
0
        { ImGuiTableFlags_SizingStretchProp,  "ImGuiTableFlags_SizingStretchProp",  "Columns default to _WidthStretch with weights proportional to their widths." },
5569
0
        { ImGuiTableFlags_SizingStretchSame,  "ImGuiTableFlags_SizingStretchSame",  "Columns default to _WidthStretch with same weights." }
5570
0
    };
5571
0
    int idx;
5572
0
    for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++)
5573
0
        if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))
5574
0
            break;
5575
0
    const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen("ImGuiTableFlags") : 0) : "";
5576
0
    if (ImGui::BeginCombo("Sizing Policy", preview_text))
5577
0
    {
5578
0
        for (int n = 0; n < IM_ARRAYSIZE(policies); n++)
5579
0
            if (ImGui::Selectable(policies[n].Name, idx == n))
5580
0
                *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;
5581
0
        ImGui::EndCombo();
5582
0
    }
5583
0
    ImGui::SameLine();
5584
0
    ImGui::TextDisabled("(?)");
5585
0
    if (ImGui::BeginItemTooltip())
5586
0
    {
5587
0
        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f);
5588
0
        for (int m = 0; m < IM_ARRAYSIZE(policies); m++)
5589
0
        {
5590
0
            ImGui::Separator();
5591
0
            ImGui::Text("%s:", policies[m].Name);
5592
0
            ImGui::Separator();
5593
0
            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);
5594
0
            ImGui::TextUnformatted(policies[m].Tooltip);
5595
0
        }
5596
0
        ImGui::PopTextWrapPos();
5597
0
        ImGui::EndTooltip();
5598
0
    }
5599
0
}
5600
5601
static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)
5602
0
{
5603
0
    ImGui::CheckboxFlags("_Disabled", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker("Master disable flag (also hide from context menu)");
5604
0
    ImGui::CheckboxFlags("_DefaultHide", p_flags, ImGuiTableColumnFlags_DefaultHide);
5605
0
    ImGui::CheckboxFlags("_DefaultSort", p_flags, ImGuiTableColumnFlags_DefaultSort);
5606
0
    if (ImGui::CheckboxFlags("_WidthStretch", p_flags, ImGuiTableColumnFlags_WidthStretch))
5607
0
        *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);
5608
0
    if (ImGui::CheckboxFlags("_WidthFixed", p_flags, ImGuiTableColumnFlags_WidthFixed))
5609
0
        *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);
5610
0
    ImGui::CheckboxFlags("_NoResize", p_flags, ImGuiTableColumnFlags_NoResize);
5611
0
    ImGui::CheckboxFlags("_NoReorder", p_flags, ImGuiTableColumnFlags_NoReorder);
5612
0
    ImGui::CheckboxFlags("_NoHide", p_flags, ImGuiTableColumnFlags_NoHide);
5613
0
    ImGui::CheckboxFlags("_NoClip", p_flags, ImGuiTableColumnFlags_NoClip);
5614
0
    ImGui::CheckboxFlags("_NoSort", p_flags, ImGuiTableColumnFlags_NoSort);
5615
0
    ImGui::CheckboxFlags("_NoSortAscending", p_flags, ImGuiTableColumnFlags_NoSortAscending);
5616
0
    ImGui::CheckboxFlags("_NoSortDescending", p_flags, ImGuiTableColumnFlags_NoSortDescending);
5617
0
    ImGui::CheckboxFlags("_NoHeaderLabel", p_flags, ImGuiTableColumnFlags_NoHeaderLabel);
5618
0
    ImGui::CheckboxFlags("_NoHeaderWidth", p_flags, ImGuiTableColumnFlags_NoHeaderWidth);
5619
0
    ImGui::CheckboxFlags("_PreferSortAscending", p_flags, ImGuiTableColumnFlags_PreferSortAscending);
5620
0
    ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending);
5621
0
    ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0");
5622
0
    ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0");
5623
0
    ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader);
5624
0
}
5625
5626
static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)
5627
0
{
5628
0
    ImGui::CheckboxFlags("_IsEnabled", &flags, ImGuiTableColumnFlags_IsEnabled);
5629
0
    ImGui::CheckboxFlags("_IsVisible", &flags, ImGuiTableColumnFlags_IsVisible);
5630
0
    ImGui::CheckboxFlags("_IsSorted", &flags, ImGuiTableColumnFlags_IsSorted);
5631
0
    ImGui::CheckboxFlags("_IsHovered", &flags, ImGuiTableColumnFlags_IsHovered);
5632
0
}
5633
5634
//-----------------------------------------------------------------------------
5635
// [SECTION] DemoWindowTables()
5636
//-----------------------------------------------------------------------------
5637
5638
static void DemoWindowTables()
5639
0
{
5640
    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);
5641
0
    IMGUI_DEMO_MARKER("Tables");
5642
0
    if (!ImGui::CollapsingHeader("Tables & Columns"))
5643
0
        return;
5644
5645
    // Using those as a base value to create width/height that are factor of the size of our font
5646
0
    const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
5647
0
    const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
5648
5649
0
    ImGui::PushID("Tables");
5650
5651
0
    int open_action = -1;
5652
0
    if (ImGui::Button("Expand all"))
5653
0
        open_action = 1;
5654
0
    ImGui::SameLine();
5655
0
    if (ImGui::Button("Collapse all"))
5656
0
        open_action = 0;
5657
0
    ImGui::SameLine();
5658
5659
    // Options
5660
0
    static bool disable_indent = false;
5661
0
    ImGui::Checkbox("Disable tree indentation", &disable_indent);
5662
0
    ImGui::SameLine();
5663
0
    HelpMarker("Disable the indenting of tree nodes so demo tables can use the full window width.");
5664
0
    ImGui::Separator();
5665
0
    if (disable_indent)
5666
0
        ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);
5667
5668
    // About Styling of tables
5669
    // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.
5670
    // There are however a few settings that a shared and part of the ImGuiStyle structure:
5671
    //   style.CellPadding                          // Padding within each cell
5672
    //   style.Colors[ImGuiCol_TableHeaderBg]       // Table header background
5673
    //   style.Colors[ImGuiCol_TableBorderStrong]   // Table outer and header borders
5674
    //   style.Colors[ImGuiCol_TableBorderLight]    // Table inner borders
5675
    //   style.Colors[ImGuiCol_TableRowBg]          // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)
5676
    //   style.Colors[ImGuiCol_TableRowBgAlt]       // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)
5677
5678
    // Demos
5679
0
    if (open_action != -1)
5680
0
        ImGui::SetNextItemOpen(open_action != 0);
5681
0
    IMGUI_DEMO_MARKER("Tables/Basic");
5682
0
    if (ImGui::TreeNode("Basic"))
5683
0
    {
5684
        // Here we will showcase three different ways to output a table.
5685
        // They are very simple variations of a same thing!
5686
5687
        // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.
5688
        // In many situations, this is the most flexible and easy to use pattern.
5689
0
        HelpMarker("Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.");
5690
0
        if (ImGui::BeginTable("table1", 3))
5691
0
        {
5692
0
            for (int row = 0; row < 4; row++)
5693
0
            {
5694
0
                ImGui::TableNextRow();
5695
0
                for (int column = 0; column < 3; column++)
5696
0
                {
5697
0
                    ImGui::TableSetColumnIndex(column);
5698
0
                    ImGui::Text("Row %d Column %d", row, column);
5699
0
                }
5700
0
            }
5701
0
            ImGui::EndTable();
5702
0
        }
5703
5704
        // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().
5705
        // This is generally more convenient when you have code manually submitting the contents of each column.
5706
0
        HelpMarker("Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.");
5707
0
        if (ImGui::BeginTable("table2", 3))
5708
0
        {
5709
0
            for (int row = 0; row < 4; row++)
5710
0
            {
5711
0
                ImGui::TableNextRow();
5712
0
                ImGui::TableNextColumn();
5713
0
                ImGui::Text("Row %d", row);
5714
0
                ImGui::TableNextColumn();
5715
0
                ImGui::Text("Some contents");
5716
0
                ImGui::TableNextColumn();
5717
0
                ImGui::Text("123.456");
5718
0
            }
5719
0
            ImGui::EndTable();
5720
0
        }
5721
5722
        // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),
5723
        // as TableNextColumn() will automatically wrap around and create new rows as needed.
5724
        // This is generally more convenient when your cells all contains the same type of data.
5725
0
        HelpMarker(
5726
0
            "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains "
5727
0
            "the same type of contents.\n This is also more similar to the old NextColumn() function of the "
5728
0
            "Columns API, and provided to facilitate the Columns->Tables API transition.");
5729
0
        if (ImGui::BeginTable("table3", 3))
5730
0
        {
5731
0
            for (int item = 0; item < 14; item++)
5732
0
            {
5733
0
                ImGui::TableNextColumn();
5734
0
                ImGui::Text("Item %d", item);
5735
0
            }
5736
0
            ImGui::EndTable();
5737
0
        }
5738
5739
0
        ImGui::TreePop();
5740
0
    }
5741
5742
0
    if (open_action != -1)
5743
0
        ImGui::SetNextItemOpen(open_action != 0);
5744
0
    IMGUI_DEMO_MARKER("Tables/Borders, background");
5745
0
    if (ImGui::TreeNode("Borders, background"))
5746
0
    {
5747
        // Expose a few Borders related flags interactively
5748
0
        enum ContentsType { CT_Text, CT_FillButton };
5749
0
        static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
5750
0
        static bool display_headers = false;
5751
0
        static int contents_type = CT_Text;
5752
5753
0
        PushStyleCompact();
5754
0
        ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
5755
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
5756
0
        ImGui::SameLine(); HelpMarker("ImGuiTableFlags_Borders\n = ImGuiTableFlags_BordersInnerV\n | ImGuiTableFlags_BordersOuterV\n | ImGuiTableFlags_BordersInnerH\n | ImGuiTableFlags_BordersOuterH");
5757
0
        ImGui::Indent();
5758
5759
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
5760
0
        ImGui::Indent();
5761
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
5762
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
5763
0
        ImGui::Unindent();
5764
5765
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
5766
0
        ImGui::Indent();
5767
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
5768
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
5769
0
        ImGui::Unindent();
5770
5771
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags, ImGuiTableFlags_BordersOuter);
5772
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags, ImGuiTableFlags_BordersInner);
5773
0
        ImGui::Unindent();
5774
5775
0
        ImGui::AlignTextToFramePadding(); ImGui::Text("Cell contents:");
5776
0
        ImGui::SameLine(); ImGui::RadioButton("Text", &contents_type, CT_Text);
5777
0
        ImGui::SameLine(); ImGui::RadioButton("FillButton", &contents_type, CT_FillButton);
5778
0
        ImGui::Checkbox("Display headers", &display_headers);
5779
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
5780
0
        PopStyleCompact();
5781
5782
0
        if (ImGui::BeginTable("table1", 3, flags))
5783
0
        {
5784
            // Display headers so we can inspect their interaction with borders
5785
            // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them now. See other sections for details)
5786
0
            if (display_headers)
5787
0
            {
5788
0
                ImGui::TableSetupColumn("One");
5789
0
                ImGui::TableSetupColumn("Two");
5790
0
                ImGui::TableSetupColumn("Three");
5791
0
                ImGui::TableHeadersRow();
5792
0
            }
5793
5794
0
            for (int row = 0; row < 5; row++)
5795
0
            {
5796
0
                ImGui::TableNextRow();
5797
0
                for (int column = 0; column < 3; column++)
5798
0
                {
5799
0
                    ImGui::TableSetColumnIndex(column);
5800
0
                    char buf[32];
5801
0
                    sprintf(buf, "Hello %d,%d", column, row);
5802
0
                    if (contents_type == CT_Text)
5803
0
                        ImGui::TextUnformatted(buf);
5804
0
                    else if (contents_type == CT_FillButton)
5805
0
                        ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
5806
0
                }
5807
0
            }
5808
0
            ImGui::EndTable();
5809
0
        }
5810
0
        ImGui::TreePop();
5811
0
    }
5812
5813
0
    if (open_action != -1)
5814
0
        ImGui::SetNextItemOpen(open_action != 0);
5815
0
    IMGUI_DEMO_MARKER("Tables/Resizable, stretch");
5816
0
    if (ImGui::TreeNode("Resizable, stretch"))
5817
0
    {
5818
        // By default, if we don't enable ScrollX the sizing policy for each column is "Stretch"
5819
        // All columns maintain a sizing weight, and they will occupy all available width.
5820
0
        static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5821
0
        PushStyleCompact();
5822
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
5823
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
5824
0
        ImGui::SameLine(); HelpMarker(
5825
0
            "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, "
5826
0
            "this is why the resize borders are still showing when unchecking this.");
5827
0
        PopStyleCompact();
5828
5829
0
        if (ImGui::BeginTable("table1", 3, flags))
5830
0
        {
5831
0
            for (int row = 0; row < 5; row++)
5832
0
            {
5833
0
                ImGui::TableNextRow();
5834
0
                for (int column = 0; column < 3; column++)
5835
0
                {
5836
0
                    ImGui::TableSetColumnIndex(column);
5837
0
                    ImGui::Text("Hello %d,%d", column, row);
5838
0
                }
5839
0
            }
5840
0
            ImGui::EndTable();
5841
0
        }
5842
0
        ImGui::TreePop();
5843
0
    }
5844
5845
0
    if (open_action != -1)
5846
0
        ImGui::SetNextItemOpen(open_action != 0);
5847
0
    IMGUI_DEMO_MARKER("Tables/Resizable, fixed");
5848
0
    if (ImGui::TreeNode("Resizable, fixed"))
5849
0
    {
5850
        // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)
5851
        // So columns will adopt the "Fixed" policy and will maintain a fixed width regardless of the whole available width (unless table is small)
5852
        // If there is not enough available width to fit all columns, they will however be resized down.
5853
        // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings
5854
0
        HelpMarker(
5855
0
            "Using _Resizable + _SizingFixedFit flags.\n"
5856
0
            "Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\n\n"
5857
0
            "Double-click a column border to auto-fit the column to its contents.");
5858
0
        PushStyleCompact();
5859
0
        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
5860
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
5861
0
        PopStyleCompact();
5862
5863
0
        if (ImGui::BeginTable("table1", 3, flags))
5864
0
        {
5865
0
            for (int row = 0; row < 5; row++)
5866
0
            {
5867
0
                ImGui::TableNextRow();
5868
0
                for (int column = 0; column < 3; column++)
5869
0
                {
5870
0
                    ImGui::TableSetColumnIndex(column);
5871
0
                    ImGui::Text("Hello %d,%d", column, row);
5872
0
                }
5873
0
            }
5874
0
            ImGui::EndTable();
5875
0
        }
5876
0
        ImGui::TreePop();
5877
0
    }
5878
5879
0
    if (open_action != -1)
5880
0
        ImGui::SetNextItemOpen(open_action != 0);
5881
0
    IMGUI_DEMO_MARKER("Tables/Resizable, mixed");
5882
0
    if (ImGui::TreeNode("Resizable, mixed"))
5883
0
    {
5884
0
        HelpMarker(
5885
0
            "Using TableSetupColumn() to alter resizing policy on a per-column basis.\n\n"
5886
0
            "When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.");
5887
0
        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
5888
5889
0
        if (ImGui::BeginTable("table1", 3, flags))
5890
0
        {
5891
0
            ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
5892
0
            ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
5893
0
            ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthStretch);
5894
0
            ImGui::TableHeadersRow();
5895
0
            for (int row = 0; row < 5; row++)
5896
0
            {
5897
0
                ImGui::TableNextRow();
5898
0
                for (int column = 0; column < 3; column++)
5899
0
                {
5900
0
                    ImGui::TableSetColumnIndex(column);
5901
0
                    ImGui::Text("%s %d,%d", (column == 2) ? "Stretch" : "Fixed", column, row);
5902
0
                }
5903
0
            }
5904
0
            ImGui::EndTable();
5905
0
        }
5906
0
        if (ImGui::BeginTable("table2", 6, flags))
5907
0
        {
5908
0
            ImGui::TableSetupColumn("AAA", ImGuiTableColumnFlags_WidthFixed);
5909
0
            ImGui::TableSetupColumn("BBB", ImGuiTableColumnFlags_WidthFixed);
5910
0
            ImGui::TableSetupColumn("CCC", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);
5911
0
            ImGui::TableSetupColumn("DDD", ImGuiTableColumnFlags_WidthStretch);
5912
0
            ImGui::TableSetupColumn("EEE", ImGuiTableColumnFlags_WidthStretch);
5913
0
            ImGui::TableSetupColumn("FFF", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);
5914
0
            ImGui::TableHeadersRow();
5915
0
            for (int row = 0; row < 5; row++)
5916
0
            {
5917
0
                ImGui::TableNextRow();
5918
0
                for (int column = 0; column < 6; column++)
5919
0
                {
5920
0
                    ImGui::TableSetColumnIndex(column);
5921
0
                    ImGui::Text("%s %d,%d", (column >= 3) ? "Stretch" : "Fixed", column, row);
5922
0
                }
5923
0
            }
5924
0
            ImGui::EndTable();
5925
0
        }
5926
0
        ImGui::TreePop();
5927
0
    }
5928
5929
0
    if (open_action != -1)
5930
0
        ImGui::SetNextItemOpen(open_action != 0);
5931
0
    IMGUI_DEMO_MARKER("Tables/Reorderable, hideable, with headers");
5932
0
    if (ImGui::TreeNode("Reorderable, hideable, with headers"))
5933
0
    {
5934
0
        HelpMarker(
5935
0
            "Click and drag column headers to reorder columns.\n\n"
5936
0
            "Right-click on a header to open a context menu.");
5937
0
        static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;
5938
0
        PushStyleCompact();
5939
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
5940
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
5941
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
5942
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody);
5943
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
5944
0
        ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
5945
0
        PopStyleCompact();
5946
5947
0
        if (ImGui::BeginTable("table1", 3, flags))
5948
0
        {
5949
            // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.
5950
            // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)
5951
0
            ImGui::TableSetupColumn("One");
5952
0
            ImGui::TableSetupColumn("Two");
5953
0
            ImGui::TableSetupColumn("Three");
5954
0
            ImGui::TableHeadersRow();
5955
0
            for (int row = 0; row < 6; row++)
5956
0
            {
5957
0
                ImGui::TableNextRow();
5958
0
                for (int column = 0; column < 3; column++)
5959
0
                {
5960
0
                    ImGui::TableSetColumnIndex(column);
5961
0
                    ImGui::Text("Hello %d,%d", column, row);
5962
0
                }
5963
0
            }
5964
0
            ImGui::EndTable();
5965
0
        }
5966
5967
        // Use outer_size.x == 0.0f instead of default to make the table as tight as possible
5968
        // (only valid when no scrolling and no stretch column)
5969
0
        if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f)))
5970
0
        {
5971
0
            ImGui::TableSetupColumn("One");
5972
0
            ImGui::TableSetupColumn("Two");
5973
0
            ImGui::TableSetupColumn("Three");
5974
0
            ImGui::TableHeadersRow();
5975
0
            for (int row = 0; row < 6; row++)
5976
0
            {
5977
0
                ImGui::TableNextRow();
5978
0
                for (int column = 0; column < 3; column++)
5979
0
                {
5980
0
                    ImGui::TableSetColumnIndex(column);
5981
0
                    ImGui::Text("Fixed %d,%d", column, row);
5982
0
                }
5983
0
            }
5984
0
            ImGui::EndTable();
5985
0
        }
5986
0
        ImGui::TreePop();
5987
0
    }
5988
5989
0
    if (open_action != -1)
5990
0
        ImGui::SetNextItemOpen(open_action != 0);
5991
0
    IMGUI_DEMO_MARKER("Tables/Padding");
5992
0
    if (ImGui::TreeNode("Padding"))
5993
0
    {
5994
        // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.
5995
        // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.
5996
0
        HelpMarker(
5997
0
            "We often want outer padding activated when any using features which makes the edges of a column visible:\n"
5998
0
            "e.g.:\n"
5999
0
            "- BorderOuterV\n"
6000
0
            "- any form of row selection\n"
6001
0
            "Because of this, activating BorderOuterV sets the default to PadOuterX. "
6002
0
            "Using PadOuterX or NoPadOuterX you can override the default.\n\n"
6003
0
            "Actual padding values are using style.CellPadding.\n\n"
6004
0
            "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.");
6005
6006
0
        static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;
6007
0
        PushStyleCompact();
6008
0
        ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags1, ImGuiTableFlags_PadOuterX);
6009
0
        ImGui::SameLine(); HelpMarker("Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)");
6010
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags1, ImGuiTableFlags_NoPadOuterX);
6011
0
        ImGui::SameLine(); HelpMarker("Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)");
6012
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags1, ImGuiTableFlags_NoPadInnerX);
6013
0
        ImGui::SameLine(); HelpMarker("Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)");
6014
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags1, ImGuiTableFlags_BordersOuterV);
6015
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags1, ImGuiTableFlags_BordersInnerV);
6016
0
        static bool show_headers = false;
6017
0
        ImGui::Checkbox("show_headers", &show_headers);
6018
0
        PopStyleCompact();
6019
6020
0
        if (ImGui::BeginTable("table_padding", 3, flags1))
6021
0
        {
6022
0
            if (show_headers)
6023
0
            {
6024
0
                ImGui::TableSetupColumn("One");
6025
0
                ImGui::TableSetupColumn("Two");
6026
0
                ImGui::TableSetupColumn("Three");
6027
0
                ImGui::TableHeadersRow();
6028
0
            }
6029
6030
0
            for (int row = 0; row < 5; row++)
6031
0
            {
6032
0
                ImGui::TableNextRow();
6033
0
                for (int column = 0; column < 3; column++)
6034
0
                {
6035
0
                    ImGui::TableSetColumnIndex(column);
6036
0
                    if (row == 0)
6037
0
                    {
6038
0
                        ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
6039
0
                    }
6040
0
                    else
6041
0
                    {
6042
0
                        char buf[32];
6043
0
                        sprintf(buf, "Hello %d,%d", column, row);
6044
0
                        ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));
6045
0
                    }
6046
                    //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)
6047
                    //    ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));
6048
0
                }
6049
0
            }
6050
0
            ImGui::EndTable();
6051
0
        }
6052
6053
        // Second example: set style.CellPadding to (0.0) or a custom value.
6054
        // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...
6055
0
        HelpMarker("Setting style.CellPadding to (0,0) or a custom value.");
6056
0
        static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;
6057
0
        static ImVec2 cell_padding(0.0f, 0.0f);
6058
0
        static bool show_widget_frame_bg = true;
6059
6060
0
        PushStyleCompact();
6061
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags2, ImGuiTableFlags_Borders);
6062
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags2, ImGuiTableFlags_BordersH);
6063
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags2, ImGuiTableFlags_BordersV);
6064
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersInner", &flags2, ImGuiTableFlags_BordersInner);
6065
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuter", &flags2, ImGuiTableFlags_BordersOuter);
6066
0
        ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags2, ImGuiTableFlags_RowBg);
6067
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags2, ImGuiTableFlags_Resizable);
6068
0
        ImGui::Checkbox("show_widget_frame_bg", &show_widget_frame_bg);
6069
0
        ImGui::SliderFloat2("CellPadding", &cell_padding.x, 0.0f, 10.0f, "%.0f");
6070
0
        PopStyleCompact();
6071
6072
0
        ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);
6073
0
        if (ImGui::BeginTable("table_padding_2", 3, flags2))
6074
0
        {
6075
0
            static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells
6076
0
            static bool init = true;
6077
0
            if (!show_widget_frame_bg)
6078
0
                ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
6079
0
            for (int cell = 0; cell < 3 * 5; cell++)
6080
0
            {
6081
0
                ImGui::TableNextColumn();
6082
0
                if (init)
6083
0
                    strcpy(text_bufs[cell], "edit me");
6084
0
                ImGui::SetNextItemWidth(-FLT_MIN);
6085
0
                ImGui::PushID(cell);
6086
0
                ImGui::InputText("##cell", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell]));
6087
0
                ImGui::PopID();
6088
0
            }
6089
0
            if (!show_widget_frame_bg)
6090
0
                ImGui::PopStyleColor();
6091
0
            init = false;
6092
0
            ImGui::EndTable();
6093
0
        }
6094
0
        ImGui::PopStyleVar();
6095
6096
0
        ImGui::TreePop();
6097
0
    }
6098
6099
0
    if (open_action != -1)
6100
0
        ImGui::SetNextItemOpen(open_action != 0);
6101
0
    IMGUI_DEMO_MARKER("Tables/Explicit widths");
6102
0
    if (ImGui::TreeNode("Sizing policies"))
6103
0
    {
6104
0
        static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6105
0
        PushStyleCompact();
6106
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
6107
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags1, ImGuiTableFlags_NoHostExtendX);
6108
0
        PopStyleCompact();
6109
6110
0
        static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };
6111
0
        for (int table_n = 0; table_n < 4; table_n++)
6112
0
        {
6113
0
            ImGui::PushID(table_n);
6114
0
            ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);
6115
0
            EditTableSizingFlags(&sizing_policy_flags[table_n]);
6116
6117
            // To make it easier to understand the different sizing policy,
6118
            // For each policy: we display one table where the columns have equal contents width,
6119
            // and one where the columns have different contents width.
6120
0
            if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1))
6121
0
            {
6122
0
                for (int row = 0; row < 3; row++)
6123
0
                {
6124
0
                    ImGui::TableNextRow();
6125
0
                    ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6126
0
                    ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6127
0
                    ImGui::TableNextColumn(); ImGui::Text("Oh dear");
6128
0
                }
6129
0
                ImGui::EndTable();
6130
0
            }
6131
0
            if (ImGui::BeginTable("table2", 3, sizing_policy_flags[table_n] | flags1))
6132
0
            {
6133
0
                for (int row = 0; row < 3; row++)
6134
0
                {
6135
0
                    ImGui::TableNextRow();
6136
0
                    ImGui::TableNextColumn(); ImGui::Text("AAAA");
6137
0
                    ImGui::TableNextColumn(); ImGui::Text("BBBBBBBB");
6138
0
                    ImGui::TableNextColumn(); ImGui::Text("CCCCCCCCCCCC");
6139
0
                }
6140
0
                ImGui::EndTable();
6141
0
            }
6142
0
            ImGui::PopID();
6143
0
        }
6144
6145
0
        ImGui::Spacing();
6146
0
        ImGui::TextUnformatted("Advanced");
6147
0
        ImGui::SameLine();
6148
0
        HelpMarker(
6149
0
            "This section allows you to interact and see the effect of various sizing policies "
6150
0
            "depending on whether Scroll is enabled and the contents of your columns.");
6151
6152
0
        enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };
6153
0
        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;
6154
0
        static int contents_type = CT_ShowWidth;
6155
0
        static int column_count = 3;
6156
6157
0
        PushStyleCompact();
6158
0
        ImGui::PushID("Advanced");
6159
0
        ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
6160
0
        EditTableSizingFlags(&flags);
6161
0
        ImGui::Combo("Contents", &contents_type, "Show width\0Short Text\0Long Text\0Button\0Fill Button\0InputText\0");
6162
0
        if (contents_type == CT_FillButton)
6163
0
        {
6164
0
            ImGui::SameLine();
6165
0
            HelpMarker(
6166
0
                "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop "
6167
0
                "where contents width can feed into auto-column width can feed into contents width.");
6168
0
        }
6169
0
        ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp);
6170
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6171
0
        ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
6172
0
        ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.");
6173
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
6174
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6175
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
6176
0
        ImGui::PopItemWidth();
6177
0
        ImGui::PopID();
6178
0
        PopStyleCompact();
6179
6180
0
        if (ImGui::BeginTable("table2", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))
6181
0
        {
6182
0
            for (int cell = 0; cell < 10 * column_count; cell++)
6183
0
            {
6184
0
                ImGui::TableNextColumn();
6185
0
                int column = ImGui::TableGetColumnIndex();
6186
0
                int row = ImGui::TableGetRowIndex();
6187
6188
0
                ImGui::PushID(cell);
6189
0
                char label[32];
6190
0
                static char text_buf[32] = "";
6191
0
                sprintf(label, "Hello %d,%d", column, row);
6192
0
                switch (contents_type)
6193
0
                {
6194
0
                case CT_ShortText:  ImGui::TextUnformatted(label); break;
6195
0
                case CT_LongText:   ImGui::Text("Some %s text %d,%d\nOver two lines..", column == 0 ? "long" : "longeeer", column, row); break;
6196
0
                case CT_ShowWidth:  ImGui::Text("W: %.1f", ImGui::GetContentRegionAvail().x); break;
6197
0
                case CT_Button:     ImGui::Button(label); break;
6198
0
                case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;
6199
0
                case CT_InputText:  ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText("##", text_buf, IM_ARRAYSIZE(text_buf)); break;
6200
0
                }
6201
0
                ImGui::PopID();
6202
0
            }
6203
0
            ImGui::EndTable();
6204
0
        }
6205
0
        ImGui::TreePop();
6206
0
    }
6207
6208
0
    if (open_action != -1)
6209
0
        ImGui::SetNextItemOpen(open_action != 0);
6210
0
    IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping");
6211
0
    if (ImGui::TreeNode("Vertical scrolling, with clipping"))
6212
0
    {
6213
0
        HelpMarker(
6214
0
            "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n"
6215
0
            "We also demonstrate using ImGuiListClipper to virtualize the submission of many items.");
6216
0
        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6217
6218
0
        PushStyleCompact();
6219
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6220
0
        PopStyleCompact();
6221
6222
        // When using ScrollX or ScrollY we need to specify a size for our table container!
6223
        // Otherwise by default the table will fit all available space, like a BeginChild() call.
6224
0
        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6225
0
        if (ImGui::BeginTable("table_scrolly", 3, flags, outer_size))
6226
0
        {
6227
0
            ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
6228
0
            ImGui::TableSetupColumn("One", ImGuiTableColumnFlags_None);
6229
0
            ImGui::TableSetupColumn("Two", ImGuiTableColumnFlags_None);
6230
0
            ImGui::TableSetupColumn("Three", ImGuiTableColumnFlags_None);
6231
0
            ImGui::TableHeadersRow();
6232
6233
            // Demonstrate using clipper for large vertical lists
6234
0
            ImGuiListClipper clipper;
6235
0
            clipper.Begin(1000);
6236
0
            while (clipper.Step())
6237
0
            {
6238
0
                for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)
6239
0
                {
6240
0
                    ImGui::TableNextRow();
6241
0
                    for (int column = 0; column < 3; column++)
6242
0
                    {
6243
0
                        ImGui::TableSetColumnIndex(column);
6244
0
                        ImGui::Text("Hello %d,%d", column, row);
6245
0
                    }
6246
0
                }
6247
0
            }
6248
0
            ImGui::EndTable();
6249
0
        }
6250
0
        ImGui::TreePop();
6251
0
    }
6252
6253
0
    if (open_action != -1)
6254
0
        ImGui::SetNextItemOpen(open_action != 0);
6255
0
    IMGUI_DEMO_MARKER("Tables/Horizontal scrolling");
6256
0
    if (ImGui::TreeNode("Horizontal scrolling"))
6257
0
    {
6258
0
        HelpMarker(
6259
0
            "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, "
6260
0
            "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n"
6261
0
            "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, "
6262
0
            "because the container window won't automatically extend vertically to fix contents "
6263
0
            "(this may be improved in future versions).");
6264
0
        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;
6265
0
        static int freeze_cols = 1;
6266
0
        static int freeze_rows = 1;
6267
6268
0
        PushStyleCompact();
6269
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
6270
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
6271
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
6272
0
        ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6273
0
        ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
6274
0
        ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
6275
0
        ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
6276
0
        PopStyleCompact();
6277
6278
        // When using ScrollX or ScrollY we need to specify a size for our table container!
6279
        // Otherwise by default the table will fit all available space, like a BeginChild() call.
6280
0
        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);
6281
0
        if (ImGui::BeginTable("table_scrollx", 7, flags, outer_size))
6282
0
        {
6283
0
            ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
6284
0
            ImGui::TableSetupColumn("Line #", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()
6285
0
            ImGui::TableSetupColumn("One");
6286
0
            ImGui::TableSetupColumn("Two");
6287
0
            ImGui::TableSetupColumn("Three");
6288
0
            ImGui::TableSetupColumn("Four");
6289
0
            ImGui::TableSetupColumn("Five");
6290
0
            ImGui::TableSetupColumn("Six");
6291
0
            ImGui::TableHeadersRow();
6292
0
            for (int row = 0; row < 20; row++)
6293
0
            {
6294
0
                ImGui::TableNextRow();
6295
0
                for (int column = 0; column < 7; column++)
6296
0
                {
6297
                    // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.
6298
                    // Because here we know that:
6299
                    // - A) all our columns are contributing the same to row height
6300
                    // - B) column 0 is always visible,
6301
                    // We only always submit this one column and can skip others.
6302
                    // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().
6303
0
                    if (!ImGui::TableSetColumnIndex(column) && column > 0)
6304
0
                        continue;
6305
0
                    if (column == 0)
6306
0
                        ImGui::Text("Line %d", row);
6307
0
                    else
6308
0
                        ImGui::Text("Hello world %d,%d", column, row);
6309
0
                }
6310
0
            }
6311
0
            ImGui::EndTable();
6312
0
        }
6313
6314
0
        ImGui::Spacing();
6315
0
        ImGui::TextUnformatted("Stretch + ScrollX");
6316
0
        ImGui::SameLine();
6317
0
        HelpMarker(
6318
0
            "Showcase using Stretch columns + ScrollX together: "
6319
0
            "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n"
6320
0
            "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns "
6321
0
            "along with ScrollX doesn't make sense.");
6322
0
        static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;
6323
0
        static float inner_width = 1000.0f;
6324
0
        PushStyleCompact();
6325
0
        ImGui::PushID("flags3");
6326
0
        ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);
6327
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags2, ImGuiTableFlags_ScrollX);
6328
0
        ImGui::DragFloat("inner_width", &inner_width, 1.0f, 0.0f, FLT_MAX, "%.1f");
6329
0
        ImGui::PopItemWidth();
6330
0
        ImGui::PopID();
6331
0
        PopStyleCompact();
6332
0
        if (ImGui::BeginTable("table2", 7, flags2, outer_size, inner_width))
6333
0
        {
6334
0
            for (int cell = 0; cell < 20 * 7; cell++)
6335
0
            {
6336
0
                ImGui::TableNextColumn();
6337
0
                ImGui::Text("Hello world %d,%d", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());
6338
0
            }
6339
0
            ImGui::EndTable();
6340
0
        }
6341
0
        ImGui::TreePop();
6342
0
    }
6343
6344
0
    if (open_action != -1)
6345
0
        ImGui::SetNextItemOpen(open_action != 0);
6346
0
    IMGUI_DEMO_MARKER("Tables/Columns flags");
6347
0
    if (ImGui::TreeNode("Columns flags"))
6348
0
    {
6349
        // Create a first table just to show all the options/flags we want to make visible in our example!
6350
0
        const int column_count = 3;
6351
0
        const char* column_names[column_count] = { "One", "Two", "Three" };
6352
0
        static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };
6353
0
        static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()
6354
6355
0
        if (ImGui::BeginTable("table_columns_flags_checkboxes", column_count, ImGuiTableFlags_None))
6356
0
        {
6357
0
            PushStyleCompact();
6358
0
            for (int column = 0; column < column_count; column++)
6359
0
            {
6360
0
                ImGui::TableNextColumn();
6361
0
                ImGui::PushID(column);
6362
0
                ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns
6363
0
                ImGui::Text("'%s'", column_names[column]);
6364
0
                ImGui::Spacing();
6365
0
                ImGui::Text("Input flags:");
6366
0
                EditTableColumnsFlags(&column_flags[column]);
6367
0
                ImGui::Spacing();
6368
0
                ImGui::Text("Output flags:");
6369
0
                ImGui::BeginDisabled();
6370
0
                ShowTableColumnsStatusFlags(column_flags_out[column]);
6371
0
                ImGui::EndDisabled();
6372
0
                ImGui::PopID();
6373
0
            }
6374
0
            PopStyleCompact();
6375
0
            ImGui::EndTable();
6376
0
        }
6377
6378
        // Create the real table we care about for the example!
6379
        // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above,
6380
        // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible
6381
        // + resizing the parent window down).
6382
0
        const ImGuiTableFlags flags
6383
0
            = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
6384
0
            | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV
6385
0
            | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;
6386
0
        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);
6387
0
        if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size))
6388
0
        {
6389
0
            bool has_angled_header = false;
6390
0
            for (int column = 0; column < column_count; column++)
6391
0
            {
6392
0
                has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;
6393
0
                ImGui::TableSetupColumn(column_names[column], column_flags[column]);
6394
0
            }
6395
0
            if (has_angled_header)
6396
0
                ImGui::TableAngledHeadersRow();
6397
0
            ImGui::TableHeadersRow();
6398
0
            for (int column = 0; column < column_count; column++)
6399
0
                column_flags_out[column] = ImGui::TableGetColumnFlags(column);
6400
0
            float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);
6401
0
            for (int row = 0; row < 8; row++)
6402
0
            {
6403
                // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.
6404
0
                ImGui::Indent(indent_step);
6405
0
                ImGui::TableNextRow();
6406
0
                for (int column = 0; column < column_count; column++)
6407
0
                {
6408
0
                    ImGui::TableSetColumnIndex(column);
6409
0
                    ImGui::Text("%s %s", (column == 0) ? "Indented" : "Hello", ImGui::TableGetColumnName(column));
6410
0
                }
6411
0
            }
6412
0
            ImGui::Unindent(indent_step * 8.0f);
6413
6414
0
            ImGui::EndTable();
6415
0
        }
6416
0
        ImGui::TreePop();
6417
0
    }
6418
6419
0
    if (open_action != -1)
6420
0
        ImGui::SetNextItemOpen(open_action != 0);
6421
0
    IMGUI_DEMO_MARKER("Tables/Columns widths");
6422
0
    if (ImGui::TreeNode("Columns widths"))
6423
0
    {
6424
0
        HelpMarker("Using TableSetupColumn() to setup default width.");
6425
6426
0
        static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;
6427
0
        PushStyleCompact();
6428
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags1, ImGuiTableFlags_Resizable);
6429
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize);
6430
0
        PopStyleCompact();
6431
0
        if (ImGui::BeginTable("table1", 3, flags1))
6432
0
        {
6433
            // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.
6434
0
            ImGui::TableSetupColumn("one", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f
6435
0
            ImGui::TableSetupColumn("two", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f
6436
0
            ImGui::TableSetupColumn("three", ImGuiTableColumnFlags_WidthFixed);       // Default to auto
6437
0
            ImGui::TableHeadersRow();
6438
0
            for (int row = 0; row < 4; row++)
6439
0
            {
6440
0
                ImGui::TableNextRow();
6441
0
                for (int column = 0; column < 3; column++)
6442
0
                {
6443
0
                    ImGui::TableSetColumnIndex(column);
6444
0
                    if (row == 0)
6445
0
                        ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6446
0
                    else
6447
0
                        ImGui::Text("Hello %d,%d", column, row);
6448
0
                }
6449
0
            }
6450
0
            ImGui::EndTable();
6451
0
        }
6452
6453
0
        HelpMarker(
6454
0
            "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, "
6455
0
            "fixed columns with set width may still be shrunk down if there's not enough space in the host.");
6456
6457
0
        static ImGuiTableFlags flags2 = ImGuiTableFlags_None;
6458
0
        PushStyleCompact();
6459
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags2, ImGuiTableFlags_NoKeepColumnsVisible);
6460
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags2, ImGuiTableFlags_BordersInnerV);
6461
0
        ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags2, ImGuiTableFlags_BordersOuterV);
6462
0
        PopStyleCompact();
6463
0
        if (ImGui::BeginTable("table2", 4, flags2))
6464
0
        {
6465
            // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns
6466
            // will default to ImGuiTableColumnFlags_WidthFixed.
6467
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f);
6468
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
6469
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f);
6470
0
            ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);
6471
0
            for (int row = 0; row < 5; row++)
6472
0
            {
6473
0
                ImGui::TableNextRow();
6474
0
                for (int column = 0; column < 4; column++)
6475
0
                {
6476
0
                    ImGui::TableSetColumnIndex(column);
6477
0
                    if (row == 0)
6478
0
                        ImGui::Text("(w: %5.1f)", ImGui::GetContentRegionAvail().x);
6479
0
                    else
6480
0
                        ImGui::Text("Hello %d,%d", column, row);
6481
0
                }
6482
0
            }
6483
0
            ImGui::EndTable();
6484
0
        }
6485
0
        ImGui::TreePop();
6486
0
    }
6487
6488
0
    if (open_action != -1)
6489
0
        ImGui::SetNextItemOpen(open_action != 0);
6490
0
    IMGUI_DEMO_MARKER("Tables/Nested tables");
6491
0
    if (ImGui::TreeNode("Nested tables"))
6492
0
    {
6493
0
        HelpMarker("This demonstrates embedding a table into another table cell.");
6494
6495
0
        if (ImGui::BeginTable("table_nested1", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6496
0
        {
6497
0
            ImGui::TableSetupColumn("A0");
6498
0
            ImGui::TableSetupColumn("A1");
6499
0
            ImGui::TableHeadersRow();
6500
6501
0
            ImGui::TableNextColumn();
6502
0
            ImGui::Text("A0 Row 0");
6503
0
            {
6504
0
                float rows_height = TEXT_BASE_HEIGHT * 2;
6505
0
                if (ImGui::BeginTable("table_nested2", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6506
0
                {
6507
0
                    ImGui::TableSetupColumn("B0");
6508
0
                    ImGui::TableSetupColumn("B1");
6509
0
                    ImGui::TableHeadersRow();
6510
6511
0
                    ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
6512
0
                    ImGui::TableNextColumn();
6513
0
                    ImGui::Text("B0 Row 0");
6514
0
                    ImGui::TableNextColumn();
6515
0
                    ImGui::Text("B1 Row 0");
6516
0
                    ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);
6517
0
                    ImGui::TableNextColumn();
6518
0
                    ImGui::Text("B0 Row 1");
6519
0
                    ImGui::TableNextColumn();
6520
0
                    ImGui::Text("B1 Row 1");
6521
6522
0
                    ImGui::EndTable();
6523
0
                }
6524
0
            }
6525
0
            ImGui::TableNextColumn(); ImGui::Text("A1 Row 0");
6526
0
            ImGui::TableNextColumn(); ImGui::Text("A0 Row 1");
6527
0
            ImGui::TableNextColumn(); ImGui::Text("A1 Row 1");
6528
0
            ImGui::EndTable();
6529
0
        }
6530
0
        ImGui::TreePop();
6531
0
    }
6532
6533
0
    if (open_action != -1)
6534
0
        ImGui::SetNextItemOpen(open_action != 0);
6535
0
    IMGUI_DEMO_MARKER("Tables/Row height");
6536
0
    if (ImGui::TreeNode("Row height"))
6537
0
    {
6538
0
        HelpMarker(
6539
0
            "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, "
6540
0
            "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n"
6541
0
            "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.");
6542
0
        if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_Borders))
6543
0
        {
6544
0
            for (int row = 0; row < 8; row++)
6545
0
            {
6546
0
                float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row);
6547
0
                ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);
6548
0
                ImGui::TableNextColumn();
6549
0
                ImGui::Text("min_row_height = %.2f", min_row_height);
6550
0
            }
6551
0
            ImGui::EndTable();
6552
0
        }
6553
6554
0
        HelpMarker(
6555
0
            "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n"
6556
0
            "Please note that Tables Row Height is not the same thing as Current Line Height, "
6557
0
            "as a table cell may contains multiple lines.");
6558
0
        if (ImGui::BeginTable("table_share_lineheight", 2, ImGuiTableFlags_Borders))
6559
0
        {
6560
0
            ImGui::TableNextRow();
6561
0
            ImGui::TableNextColumn();
6562
0
            ImGui::ColorButton("##1", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));
6563
0
            ImGui::TableNextColumn();
6564
0
            ImGui::Text("Line 1");
6565
0
            ImGui::Text("Line 2");
6566
6567
0
            ImGui::TableNextRow();
6568
0
            ImGui::TableNextColumn();
6569
0
            ImGui::ColorButton("##2", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));
6570
0
            ImGui::TableNextColumn();
6571
0
            ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column
6572
0
            ImGui::Text("Line 1, with SameLine(0,0)");
6573
0
            ImGui::Text("Line 2");
6574
6575
0
            ImGui::EndTable();
6576
0
        }
6577
6578
0
        HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.");
6579
0
        if (ImGui::BeginTable("table_changing_cellpadding_y", 1, ImGuiTableFlags_Borders))
6580
0
        {
6581
0
            ImGuiStyle& style = ImGui::GetStyle();
6582
0
            for (int row = 0; row < 8; row++)
6583
0
            {
6584
0
                if ((row % 3) == 2)
6585
0
                    ImGui::PushStyleVarY(ImGuiStyleVar_CellPadding, 20.0f);
6586
0
                ImGui::TableNextRow(ImGuiTableRowFlags_None);
6587
0
                ImGui::TableNextColumn();
6588
0
                ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y);
6589
0
                if ((row % 3) == 2)
6590
0
                    ImGui::PopStyleVar();
6591
0
            }
6592
0
            ImGui::EndTable();
6593
0
        }
6594
6595
0
        ImGui::TreePop();
6596
0
    }
6597
6598
0
    if (open_action != -1)
6599
0
        ImGui::SetNextItemOpen(open_action != 0);
6600
0
    IMGUI_DEMO_MARKER("Tables/Outer size");
6601
0
    if (ImGui::TreeNode("Outer size"))
6602
0
    {
6603
        // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY
6604
        // Important to that note how the two flags have slightly different behaviors!
6605
0
        ImGui::Text("Using NoHostExtendX and NoHostExtendY:");
6606
0
        PushStyleCompact();
6607
0
        static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
6608
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
6609
0
        ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.");
6610
0
        ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
6611
0
        ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.");
6612
0
        PopStyleCompact();
6613
6614
0
        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
6615
0
        if (ImGui::BeginTable("table1", 3, flags, outer_size))
6616
0
        {
6617
0
            for (int row = 0; row < 10; row++)
6618
0
            {
6619
0
                ImGui::TableNextRow();
6620
0
                for (int column = 0; column < 3; column++)
6621
0
                {
6622
0
                    ImGui::TableNextColumn();
6623
0
                    ImGui::Text("Cell %d,%d", column, row);
6624
0
                }
6625
0
            }
6626
0
            ImGui::EndTable();
6627
0
        }
6628
0
        ImGui::SameLine();
6629
0
        ImGui::Text("Hello!");
6630
6631
0
        ImGui::Spacing();
6632
6633
0
        ImGui::Text("Using explicit size:");
6634
0
        if (ImGui::BeginTable("table2", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6635
0
        {
6636
0
            for (int row = 0; row < 5; row++)
6637
0
            {
6638
0
                ImGui::TableNextRow();
6639
0
                for (int column = 0; column < 3; column++)
6640
0
                {
6641
0
                    ImGui::TableNextColumn();
6642
0
                    ImGui::Text("Cell %d,%d", column, row);
6643
0
                }
6644
0
            }
6645
0
            ImGui::EndTable();
6646
0
        }
6647
0
        ImGui::SameLine();
6648
0
        if (ImGui::BeginTable("table3", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))
6649
0
        {
6650
0
            for (int row = 0; row < 3; row++)
6651
0
            {
6652
0
                ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f);
6653
0
                for (int column = 0; column < 3; column++)
6654
0
                {
6655
0
                    ImGui::TableNextColumn();
6656
0
                    ImGui::Text("Cell %d,%d", column, row);
6657
0
                }
6658
0
            }
6659
0
            ImGui::EndTable();
6660
0
        }
6661
6662
0
        ImGui::TreePop();
6663
0
    }
6664
6665
0
    if (open_action != -1)
6666
0
        ImGui::SetNextItemOpen(open_action != 0);
6667
0
    IMGUI_DEMO_MARKER("Tables/Background color");
6668
0
    if (ImGui::TreeNode("Background color"))
6669
0
    {
6670
0
        static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;
6671
0
        static int row_bg_type = 1;
6672
0
        static int row_bg_target = 1;
6673
0
        static int cell_bg_type = 1;
6674
6675
0
        PushStyleCompact();
6676
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Borders", &flags, ImGuiTableFlags_Borders);
6677
0
        ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
6678
0
        ImGui::SameLine(); HelpMarker("ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.");
6679
0
        ImGui::Combo("row bg type", (int*)&row_bg_type, "None\0Red\0Gradient\0");
6680
0
        ImGui::Combo("row bg target", (int*)&row_bg_target, "RowBg0\0RowBg1\0"); ImGui::SameLine(); HelpMarker("Target RowBg0 to override the alternating odd/even colors,\nTarget RowBg1 to blend with them.");
6681
0
        ImGui::Combo("cell bg type", (int*)&cell_bg_type, "None\0Blue\0"); ImGui::SameLine(); HelpMarker("We are colorizing cells to B1->C2 here.");
6682
0
        IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);
6683
0
        IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);
6684
0
        IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);
6685
0
        PopStyleCompact();
6686
6687
0
        if (ImGui::BeginTable("table1", 5, flags))
6688
0
        {
6689
0
            for (int row = 0; row < 6; row++)
6690
0
            {
6691
0
                ImGui::TableNextRow();
6692
6693
                // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'
6694
                // We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag.
6695
0
                if (row_bg_type != 0)
6696
0
                {
6697
0
                    ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient?
6698
0
                    ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);
6699
0
                }
6700
6701
                // Fill cells
6702
0
                for (int column = 0; column < 5; column++)
6703
0
                {
6704
0
                    ImGui::TableSetColumnIndex(column);
6705
0
                    ImGui::Text("%c%c", 'A' + row, '0' + column);
6706
6707
                    // Change background of Cells B1->C2
6708
                    // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'
6709
                    // (the CellBg color will be blended over the RowBg and ColumnBg colors)
6710
                    // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.
6711
0
                    if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)
6712
0
                    {
6713
0
                        ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));
6714
0
                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);
6715
0
                    }
6716
0
                }
6717
0
            }
6718
0
            ImGui::EndTable();
6719
0
        }
6720
0
        ImGui::TreePop();
6721
0
    }
6722
6723
0
    if (open_action != -1)
6724
0
        ImGui::SetNextItemOpen(open_action != 0);
6725
0
    IMGUI_DEMO_MARKER("Tables/Tree view");
6726
0
    if (ImGui::TreeNode("Tree view"))
6727
0
    {
6728
0
        static ImGuiTableFlags table_flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
6729
6730
0
        static ImGuiTreeNodeFlags tree_node_flags_base = ImGuiTreeNodeFlags_SpanAllColumns | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_DrawLinesFull;
6731
0
        ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth",  &tree_node_flags_base, ImGuiTreeNodeFlags_SpanFullWidth);
6732
0
        ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanLabelWidth",  &tree_node_flags_base, ImGuiTreeNodeFlags_SpanLabelWidth);
6733
0
        ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_SpanAllColumns);
6734
0
        ImGui::CheckboxFlags("ImGuiTreeNodeFlags_LabelSpanAllColumns", &tree_node_flags_base, ImGuiTreeNodeFlags_LabelSpanAllColumns);
6735
0
        ImGui::SameLine(); HelpMarker("Useful if you know that you aren't displaying contents in other columns");
6736
6737
0
        HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns.");
6738
0
        if (ImGui::BeginTable("3ways", 3, table_flags))
6739
0
        {
6740
            // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On
6741
0
            ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide);
6742
0
            ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);
6743
0
            ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f);
6744
0
            ImGui::TableHeadersRow();
6745
6746
            // Simple storage to output a dummy file-system.
6747
0
            struct MyTreeNode
6748
0
            {
6749
0
                const char*     Name;
6750
0
                const char*     Type;
6751
0
                int             Size;
6752
0
                int             ChildIdx;
6753
0
                int             ChildCount;
6754
0
                static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)
6755
0
                {
6756
0
                    ImGui::TableNextRow();
6757
0
                    ImGui::TableNextColumn();
6758
0
                    const bool is_folder = (node->ChildCount > 0);
6759
6760
0
                    ImGuiTreeNodeFlags node_flags = tree_node_flags_base;
6761
0
                    if (node != &all_nodes[0])
6762
0
                        node_flags &= ~ImGuiTreeNodeFlags_LabelSpanAllColumns; // Only demonstrate this on the root node.
6763
6764
0
                    if (is_folder)
6765
0
                    {
6766
0
                        bool open = ImGui::TreeNodeEx(node->Name, node_flags);
6767
0
                        if ((node_flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) == 0)
6768
0
                        {
6769
0
                            ImGui::TableNextColumn();
6770
0
                            ImGui::TextDisabled("--");
6771
0
                            ImGui::TableNextColumn();
6772
0
                            ImGui::TextUnformatted(node->Type);
6773
0
                        }
6774
0
                        if (open)
6775
0
                        {
6776
0
                            for (int child_n = 0; child_n < node->ChildCount; child_n++)
6777
0
                                DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes);
6778
0
                            ImGui::TreePop();
6779
0
                        }
6780
0
                    }
6781
0
                    else
6782
0
                    {
6783
0
                        ImGui::TreeNodeEx(node->Name, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);
6784
0
                        ImGui::TableNextColumn();
6785
0
                        ImGui::Text("%d", node->Size);
6786
0
                        ImGui::TableNextColumn();
6787
0
                        ImGui::TextUnformatted(node->Type);
6788
0
                    }
6789
0
                }
6790
0
            };
6791
0
            static const MyTreeNode nodes[] =
6792
0
            {
6793
0
                { "Root with Long Name",          "Folder",       -1,       1, 3    }, // 0
6794
0
                { "Music",                        "Folder",       -1,       4, 2    }, // 1
6795
0
                { "Textures",                     "Folder",       -1,       6, 3    }, // 2
6796
0
                { "desktop.ini",                  "System file",  1024,    -1,-1    }, // 3
6797
0
                { "File1_a.wav",                  "Audio file",   123000,  -1,-1    }, // 4
6798
0
                { "File1_b.wav",                  "Audio file",   456000,  -1,-1    }, // 5
6799
0
                { "Image001.png",                 "Image file",   203128,  -1,-1    }, // 6
6800
0
                { "Copy of Image001.png",         "Image file",   203256,  -1,-1    }, // 7
6801
0
                { "Copy of Image001 (Final2).png","Image file",   203512,  -1,-1    }, // 8
6802
0
            };
6803
6804
0
            MyTreeNode::DisplayNode(&nodes[0], nodes);
6805
6806
0
            ImGui::EndTable();
6807
0
        }
6808
0
        ImGui::TreePop();
6809
0
    }
6810
6811
0
    if (open_action != -1)
6812
0
        ImGui::SetNextItemOpen(open_action != 0);
6813
0
    IMGUI_DEMO_MARKER("Tables/Item width");
6814
0
    if (ImGui::TreeNode("Item width"))
6815
0
    {
6816
0
        HelpMarker(
6817
0
            "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n"
6818
0
            "Note that on auto-resizing non-resizable fixed columns, querying the content width for "
6819
0
            "e.g. right-alignment doesn't make sense.");
6820
0
        if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders))
6821
0
        {
6822
0
            ImGui::TableSetupColumn("small");
6823
0
            ImGui::TableSetupColumn("half");
6824
0
            ImGui::TableSetupColumn("right-align");
6825
0
            ImGui::TableHeadersRow();
6826
6827
0
            for (int row = 0; row < 3; row++)
6828
0
            {
6829
0
                ImGui::TableNextRow();
6830
0
                if (row == 0)
6831
0
                {
6832
                    // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)
6833
0
                    ImGui::TableSetColumnIndex(0);
6834
0
                    ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small
6835
0
                    ImGui::TableSetColumnIndex(1);
6836
0
                    ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);
6837
0
                    ImGui::TableSetColumnIndex(2);
6838
0
                    ImGui::PushItemWidth(-FLT_MIN); // Right-aligned
6839
0
                }
6840
6841
                // Draw our contents
6842
0
                static float dummy_f = 0.0f;
6843
0
                ImGui::PushID(row);
6844
0
                ImGui::TableSetColumnIndex(0);
6845
0
                ImGui::SliderFloat("float0", &dummy_f, 0.0f, 1.0f);
6846
0
                ImGui::TableSetColumnIndex(1);
6847
0
                ImGui::SliderFloat("float1", &dummy_f, 0.0f, 1.0f);
6848
0
                ImGui::TableSetColumnIndex(2);
6849
0
                ImGui::SliderFloat("##float2", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned
6850
0
                ImGui::PopID();
6851
0
            }
6852
0
            ImGui::EndTable();
6853
0
        }
6854
0
        ImGui::TreePop();
6855
0
    }
6856
6857
    // Demonstrate using TableHeader() calls instead of TableHeadersRow()
6858
0
    if (open_action != -1)
6859
0
        ImGui::SetNextItemOpen(open_action != 0);
6860
0
    IMGUI_DEMO_MARKER("Tables/Custom headers");
6861
0
    if (ImGui::TreeNode("Custom headers"))
6862
0
    {
6863
0
        const int COLUMNS_COUNT = 3;
6864
0
        if (ImGui::BeginTable("table_custom_headers", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
6865
0
        {
6866
0
            ImGui::TableSetupColumn("Apricot");
6867
0
            ImGui::TableSetupColumn("Banana");
6868
0
            ImGui::TableSetupColumn("Cherry");
6869
6870
            // Dummy entire-column selection storage
6871
            // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.
6872
0
            static bool column_selected[3] = {};
6873
6874
            // Instead of calling TableHeadersRow() we'll submit custom headers ourselves.
6875
            // (A different approach is also possible:
6876
            //    - Specify ImGuiTableColumnFlags_NoHeaderLabel in some TableSetupColumn() call.
6877
            //    - Call TableHeadersRow() normally. This will submit TableHeader() with no name.
6878
            //    - Then call TableSetColumnIndex() to position yourself in the column and submit your stuff e.g. Checkbox().)
6879
0
            ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
6880
0
            for (int column = 0; column < COLUMNS_COUNT; column++)
6881
0
            {
6882
0
                ImGui::TableSetColumnIndex(column);
6883
0
                const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()
6884
0
                ImGui::PushID(column);
6885
0
                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
6886
0
                ImGui::Checkbox("##checkall", &column_selected[column]);
6887
0
                ImGui::PopStyleVar();
6888
0
                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
6889
0
                ImGui::TableHeader(column_name);
6890
0
                ImGui::PopID();
6891
0
            }
6892
6893
            // Submit table contents
6894
0
            for (int row = 0; row < 5; row++)
6895
0
            {
6896
0
                ImGui::TableNextRow();
6897
0
                for (int column = 0; column < 3; column++)
6898
0
                {
6899
0
                    char buf[32];
6900
0
                    sprintf(buf, "Cell %d,%d", column, row);
6901
0
                    ImGui::TableSetColumnIndex(column);
6902
0
                    ImGui::Selectable(buf, column_selected[column]);
6903
0
                }
6904
0
            }
6905
0
            ImGui::EndTable();
6906
0
        }
6907
0
        ImGui::TreePop();
6908
0
    }
6909
6910
    // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers
6911
0
    if (open_action != -1)
6912
0
        ImGui::SetNextItemOpen(open_action != 0);
6913
0
    IMGUI_DEMO_MARKER("Tables/Angled headers");
6914
0
    if (ImGui::TreeNode("Angled headers"))
6915
0
    {
6916
0
        const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" };
6917
0
        const int columns_count = IM_ARRAYSIZE(column_names);
6918
0
        const int rows_count = 12;
6919
6920
0
        static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;
6921
0
        static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed;
6922
0
        static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage
6923
0
        static int frozen_cols = 1;
6924
0
        static int frozen_rows = 2;
6925
0
        ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
6926
0
        ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
6927
0
        ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable);
6928
0
        ImGui::CheckboxFlags("_Sortable", &table_flags, ImGuiTableFlags_Sortable);
6929
0
        ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody);
6930
0
        ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);
6931
0
        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6932
0
        ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2);
6933
0
        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6934
0
        ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
6935
0
        ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth);
6936
6937
0
        if (ImGui::TreeNode("Style settings"))
6938
0
        {
6939
0
            ImGui::SameLine();
6940
0
            HelpMarker("Giving access to some ImGuiStyle value in this demo for convenience.");
6941
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6942
0
            ImGui::SliderAngle("style.TableAngledHeadersAngle", &ImGui::GetStyle().TableAngledHeadersAngle, -50.0f, +50.0f);
6943
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
6944
0
            ImGui::SliderFloat2("style.TableAngledHeadersTextAlign", (float*)&ImGui::GetStyle().TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
6945
0
            ImGui::TreePop();
6946
0
        }
6947
6948
0
        if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))
6949
0
        {
6950
0
            ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
6951
0
            for (int n = 1; n < columns_count; n++)
6952
0
                ImGui::TableSetupColumn(column_names[n], column_flags);
6953
0
            ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);
6954
6955
0
            ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.
6956
0
            ImGui::TableHeadersRow();       // Draw remaining headers and allow access to context-menu and other functions.
6957
0
            for (int row = 0; row < rows_count; row++)
6958
0
            {
6959
0
                ImGui::PushID(row);
6960
0
                ImGui::TableNextRow();
6961
0
                ImGui::TableSetColumnIndex(0);
6962
0
                ImGui::AlignTextToFramePadding();
6963
0
                ImGui::Text("Track %d", row);
6964
0
                for (int column = 1; column < columns_count; column++)
6965
0
                    if (ImGui::TableSetColumnIndex(column))
6966
0
                    {
6967
0
                        ImGui::PushID(column);
6968
0
                        ImGui::Checkbox("", &bools[row * columns_count + column]);
6969
0
                        ImGui::PopID();
6970
0
                    }
6971
0
                ImGui::PopID();
6972
0
            }
6973
0
            ImGui::EndTable();
6974
0
        }
6975
0
        ImGui::TreePop();
6976
0
    }
6977
6978
    // Demonstrate creating custom context menus inside columns,
6979
    // while playing it nice with context menus provided by TableHeadersRow()/TableHeader()
6980
0
    if (open_action != -1)
6981
0
        ImGui::SetNextItemOpen(open_action != 0);
6982
0
    IMGUI_DEMO_MARKER("Tables/Context menus");
6983
0
    if (ImGui::TreeNode("Context menus"))
6984
0
    {
6985
0
        HelpMarker(
6986
0
            "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n"
6987
0
            "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.");
6988
0
        static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;
6989
6990
0
        PushStyleCompact();
6991
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags1, ImGuiTableFlags_ContextMenuInBody);
6992
0
        PopStyleCompact();
6993
6994
        // Context Menus: first example
6995
        // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.
6996
        // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)
6997
0
        const int COLUMNS_COUNT = 3;
6998
0
        if (ImGui::BeginTable("table_context_menu", COLUMNS_COUNT, flags1))
6999
0
        {
7000
0
            ImGui::TableSetupColumn("One");
7001
0
            ImGui::TableSetupColumn("Two");
7002
0
            ImGui::TableSetupColumn("Three");
7003
7004
            // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.
7005
0
            ImGui::TableHeadersRow();
7006
7007
            // Submit dummy contents
7008
0
            for (int row = 0; row < 4; row++)
7009
0
            {
7010
0
                ImGui::TableNextRow();
7011
0
                for (int column = 0; column < COLUMNS_COUNT; column++)
7012
0
                {
7013
0
                    ImGui::TableSetColumnIndex(column);
7014
0
                    ImGui::Text("Cell %d,%d", column, row);
7015
0
                }
7016
0
            }
7017
0
            ImGui::EndTable();
7018
0
        }
7019
7020
        // Context Menus: second example
7021
        // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7022
        // [2.2] Right-click on the ".." to open a custom popup
7023
        // [2.3] Right-click in columns to open another custom popup
7024
0
        HelpMarker(
7025
0
            "Demonstrate mixing table context menu (over header), item context button (over button) "
7026
0
            "and custom per-colunm context menu (over column body).");
7027
0
        ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;
7028
0
        if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2))
7029
0
        {
7030
0
            ImGui::TableSetupColumn("One");
7031
0
            ImGui::TableSetupColumn("Two");
7032
0
            ImGui::TableSetupColumn("Three");
7033
7034
            // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.
7035
0
            ImGui::TableHeadersRow();
7036
0
            for (int row = 0; row < 4; row++)
7037
0
            {
7038
0
                ImGui::TableNextRow();
7039
0
                for (int column = 0; column < COLUMNS_COUNT; column++)
7040
0
                {
7041
                    // Submit dummy contents
7042
0
                    ImGui::TableSetColumnIndex(column);
7043
0
                    ImGui::Text("Cell %d,%d", column, row);
7044
0
                    ImGui::SameLine();
7045
7046
                    // [2.2] Right-click on the ".." to open a custom popup
7047
0
                    ImGui::PushID(row * COLUMNS_COUNT + column);
7048
0
                    ImGui::SmallButton("..");
7049
0
                    if (ImGui::BeginPopupContextItem())
7050
0
                    {
7051
0
                        ImGui::Text("This is the popup for Button(\"..\") in Cell %d,%d", column, row);
7052
0
                        if (ImGui::Button("Close"))
7053
0
                            ImGui::CloseCurrentPopup();
7054
0
                        ImGui::EndPopup();
7055
0
                    }
7056
0
                    ImGui::PopID();
7057
0
                }
7058
0
            }
7059
7060
            // [2.3] Right-click anywhere in columns to open another custom popup
7061
            // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup
7062
            // to manage popup priority as the popups triggers, here "are we hovering a column" are overlapping)
7063
0
            int hovered_column = -1;
7064
0
            for (int column = 0; column < COLUMNS_COUNT + 1; column++)
7065
0
            {
7066
0
                ImGui::PushID(column);
7067
0
                if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered)
7068
0
                    hovered_column = column;
7069
0
                if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1))
7070
0
                    ImGui::OpenPopup("MyPopup");
7071
0
                if (ImGui::BeginPopup("MyPopup"))
7072
0
                {
7073
0
                    if (column == COLUMNS_COUNT)
7074
0
                        ImGui::Text("This is a custom popup for unused space after the last column.");
7075
0
                    else
7076
0
                        ImGui::Text("This is a custom popup for Column %d", column);
7077
0
                    if (ImGui::Button("Close"))
7078
0
                        ImGui::CloseCurrentPopup();
7079
0
                    ImGui::EndPopup();
7080
0
                }
7081
0
                ImGui::PopID();
7082
0
            }
7083
7084
0
            ImGui::EndTable();
7085
0
            ImGui::Text("Hovered column: %d", hovered_column);
7086
0
        }
7087
0
        ImGui::TreePop();
7088
0
    }
7089
7090
    // Demonstrate creating multiple tables with the same ID
7091
0
    if (open_action != -1)
7092
0
        ImGui::SetNextItemOpen(open_action != 0);
7093
0
    IMGUI_DEMO_MARKER("Tables/Synced instances");
7094
0
    if (ImGui::TreeNode("Synced instances"))
7095
0
    {
7096
0
        HelpMarker("Multiple tables with the same identifier will share their settings, width, visibility, order etc.");
7097
7098
0
        static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;
7099
0
        ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
7100
0
        ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
7101
0
        ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit);
7102
0
        ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
7103
0
        for (int n = 0; n < 3; n++)
7104
0
        {
7105
0
            char buf[32];
7106
0
            sprintf(buf, "Synced Table %d", n);
7107
0
            bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen);
7108
0
            if (open && ImGui::BeginTable("Table", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))
7109
0
            {
7110
0
                ImGui::TableSetupColumn("One");
7111
0
                ImGui::TableSetupColumn("Two");
7112
0
                ImGui::TableSetupColumn("Three");
7113
0
                ImGui::TableHeadersRow();
7114
0
                const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.
7115
0
                for (int cell = 0; cell < cell_count; cell++)
7116
0
                {
7117
0
                    ImGui::TableNextColumn();
7118
0
                    ImGui::Text("this cell %d", cell);
7119
0
                }
7120
0
                ImGui::EndTable();
7121
0
            }
7122
0
        }
7123
0
        ImGui::TreePop();
7124
0
    }
7125
7126
    // Demonstrate using Sorting facilities
7127
    // This is a simplified version of the "Advanced" example, where we mostly focus on the code necessary to handle sorting.
7128
    // Note that the "Advanced" example also showcase manually triggering a sort (e.g. if item quantities have been modified)
7129
0
    static const char* template_items_names[] =
7130
0
    {
7131
0
        "Banana", "Apple", "Cherry", "Watermelon", "Grapefruit", "Strawberry", "Mango",
7132
0
        "Kiwi", "Orange", "Pineapple", "Blueberry", "Plum", "Coconut", "Pear", "Apricot"
7133
0
    };
7134
0
    if (open_action != -1)
7135
0
        ImGui::SetNextItemOpen(open_action != 0);
7136
0
    IMGUI_DEMO_MARKER("Tables/Sorting");
7137
0
    if (ImGui::TreeNode("Sorting"))
7138
0
    {
7139
        // Create item list
7140
0
        static ImVector<MyItem> items;
7141
0
        if (items.Size == 0)
7142
0
        {
7143
0
            items.resize(50, MyItem());
7144
0
            for (int n = 0; n < items.Size; n++)
7145
0
            {
7146
0
                const int template_n = n % IM_ARRAYSIZE(template_items_names);
7147
0
                MyItem& item = items[n];
7148
0
                item.ID = n;
7149
0
                item.Name = template_items_names[template_n];
7150
0
                item.Quantity = (n * n - n) % 20; // Assign default quantities
7151
0
            }
7152
0
        }
7153
7154
        // Options
7155
0
        static ImGuiTableFlags flags =
7156
0
            ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7157
0
            | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody
7158
0
            | ImGuiTableFlags_ScrollY;
7159
0
        PushStyleCompact();
7160
0
        ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
7161
0
        ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7162
0
        ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
7163
0
        ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7164
0
        PopStyleCompact();
7165
7166
0
        if (ImGui::BeginTable("table_sorting", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f))
7167
0
        {
7168
            // Declare columns
7169
            // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7170
            // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7171
            // Demonstrate using a mixture of flags among available sort-related flags:
7172
            // - ImGuiTableColumnFlags_DefaultSort
7173
            // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending
7174
            // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending
7175
0
            ImGui::TableSetupColumn("ID",       ImGuiTableColumnFlags_DefaultSort          | ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_ID);
7176
0
            ImGui::TableSetupColumn("Name",                                                  ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_Name);
7177
0
            ImGui::TableSetupColumn("Action",   ImGuiTableColumnFlags_NoSort               | ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_Action);
7178
0
            ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity);
7179
0
            ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible
7180
0
            ImGui::TableHeadersRow();
7181
7182
            // Sort our data if sort specs have been changed!
7183
0
            if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
7184
0
                if (sort_specs->SpecsDirty)
7185
0
                {
7186
0
                    MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);
7187
0
                    sort_specs->SpecsDirty = false;
7188
0
                }
7189
7190
            // Demonstrate using clipper for large vertical lists
7191
0
            ImGuiListClipper clipper;
7192
0
            clipper.Begin(items.Size);
7193
0
            while (clipper.Step())
7194
0
                for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7195
0
                {
7196
                    // Display a data item
7197
0
                    MyItem* item = &items[row_n];
7198
0
                    ImGui::PushID(item->ID);
7199
0
                    ImGui::TableNextRow();
7200
0
                    ImGui::TableNextColumn();
7201
0
                    ImGui::Text("%04d", item->ID);
7202
0
                    ImGui::TableNextColumn();
7203
0
                    ImGui::TextUnformatted(item->Name);
7204
0
                    ImGui::TableNextColumn();
7205
0
                    ImGui::SmallButton("None");
7206
0
                    ImGui::TableNextColumn();
7207
0
                    ImGui::Text("%d", item->Quantity);
7208
0
                    ImGui::PopID();
7209
0
                }
7210
0
            ImGui::EndTable();
7211
0
        }
7212
0
        ImGui::TreePop();
7213
0
    }
7214
7215
    // In this example we'll expose most table flags and settings.
7216
    // For specific flags and settings refer to the corresponding section for more detailed explanation.
7217
    // This section is mostly useful to experiment with combining certain flags or settings with each others.
7218
    //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]
7219
0
    if (open_action != -1)
7220
0
        ImGui::SetNextItemOpen(open_action != 0);
7221
0
    IMGUI_DEMO_MARKER("Tables/Advanced");
7222
0
    if (ImGui::TreeNode("Advanced"))
7223
0
    {
7224
0
        static ImGuiTableFlags flags =
7225
0
            ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable
7226
0
            | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti
7227
0
            | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody
7228
0
            | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY
7229
0
            | ImGuiTableFlags_SizingFixedFit;
7230
0
        static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;
7231
7232
0
        enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };
7233
0
        static int contents_type = CT_SelectableSpanRow;
7234
0
        const char* contents_type_names[] = { "Text", "Button", "SmallButton", "FillButton", "Selectable", "Selectable (span row)" };
7235
0
        static int freeze_cols = 1;
7236
0
        static int freeze_rows = 1;
7237
0
        static int items_count = IM_ARRAYSIZE(template_items_names) * 2;
7238
0
        static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);
7239
0
        static float row_min_height = 0.0f; // Auto
7240
0
        static float inner_width_with_scroll = 0.0f; // Auto-extend
7241
0
        static bool outer_size_enabled = true;
7242
0
        static bool show_headers = true;
7243
0
        static bool show_wrapped_text = false;
7244
        //static ImGuiTextFilter filter;
7245
        //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing
7246
0
        if (ImGui::TreeNode("Options"))
7247
0
        {
7248
            // Make the UI compact because there are so many fields
7249
0
            PushStyleCompact();
7250
0
            ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f);
7251
7252
0
            if (ImGui::TreeNodeEx("Features:", ImGuiTreeNodeFlags_DefaultOpen))
7253
0
            {
7254
0
                ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable);
7255
0
                ImGui::CheckboxFlags("ImGuiTableFlags_Reorderable", &flags, ImGuiTableFlags_Reorderable);
7256
0
                ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable);
7257
0
                ImGui::CheckboxFlags("ImGuiTableFlags_Sortable", &flags, ImGuiTableFlags_Sortable);
7258
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoSavedSettings", &flags, ImGuiTableFlags_NoSavedSettings);
7259
0
                ImGui::CheckboxFlags("ImGuiTableFlags_ContextMenuInBody", &flags, ImGuiTableFlags_ContextMenuInBody);
7260
0
                ImGui::TreePop();
7261
0
            }
7262
7263
0
            if (ImGui::TreeNodeEx("Decorations:", ImGuiTreeNodeFlags_DefaultOpen))
7264
0
            {
7265
0
                ImGui::CheckboxFlags("ImGuiTableFlags_RowBg", &flags, ImGuiTableFlags_RowBg);
7266
0
                ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV);
7267
0
                ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterV", &flags, ImGuiTableFlags_BordersOuterV);
7268
0
                ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerV", &flags, ImGuiTableFlags_BordersInnerV);
7269
0
                ImGui::CheckboxFlags("ImGuiTableFlags_BordersH", &flags, ImGuiTableFlags_BordersH);
7270
0
                ImGui::CheckboxFlags("ImGuiTableFlags_BordersOuterH", &flags, ImGuiTableFlags_BordersOuterH);
7271
0
                ImGui::CheckboxFlags("ImGuiTableFlags_BordersInnerH", &flags, ImGuiTableFlags_BordersInnerH);
7272
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body (borders will always appear in Headers)");
7273
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)");
7274
0
                ImGui::TreePop();
7275
0
            }
7276
7277
0
            if (ImGui::TreeNodeEx("Sizing:", ImGuiTreeNodeFlags_DefaultOpen))
7278
0
            {
7279
0
                EditTableSizingFlags(&flags);
7280
0
                ImGui::SameLine(); HelpMarker("In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.");
7281
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendX", &flags, ImGuiTableFlags_NoHostExtendX);
7282
0
                ImGui::SameLine(); HelpMarker("Make outer width auto-fit to columns, overriding outer_size.x value.\n\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.");
7283
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoHostExtendY", &flags, ImGuiTableFlags_NoHostExtendY);
7284
0
                ImGui::SameLine(); HelpMarker("Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\n\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.");
7285
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoKeepColumnsVisible", &flags, ImGuiTableFlags_NoKeepColumnsVisible);
7286
0
                ImGui::SameLine(); HelpMarker("Only available if ScrollX is disabled.");
7287
0
                ImGui::CheckboxFlags("ImGuiTableFlags_PreciseWidths", &flags, ImGuiTableFlags_PreciseWidths);
7288
0
                ImGui::SameLine(); HelpMarker("Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.");
7289
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoClip", &flags, ImGuiTableFlags_NoClip);
7290
0
                ImGui::SameLine(); HelpMarker("Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options.");
7291
0
                ImGui::TreePop();
7292
0
            }
7293
7294
0
            if (ImGui::TreeNodeEx("Padding:", ImGuiTreeNodeFlags_DefaultOpen))
7295
0
            {
7296
0
                ImGui::CheckboxFlags("ImGuiTableFlags_PadOuterX", &flags, ImGuiTableFlags_PadOuterX);
7297
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoPadOuterX", &flags, ImGuiTableFlags_NoPadOuterX);
7298
0
                ImGui::CheckboxFlags("ImGuiTableFlags_NoPadInnerX", &flags, ImGuiTableFlags_NoPadInnerX);
7299
0
                ImGui::TreePop();
7300
0
            }
7301
7302
0
            if (ImGui::TreeNodeEx("Scrolling:", ImGuiTreeNodeFlags_DefaultOpen))
7303
0
            {
7304
0
                ImGui::CheckboxFlags("ImGuiTableFlags_ScrollX", &flags, ImGuiTableFlags_ScrollX);
7305
0
                ImGui::SameLine();
7306
0
                ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7307
0
                ImGui::DragInt("freeze_cols", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
7308
0
                ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY);
7309
0
                ImGui::SameLine();
7310
0
                ImGui::SetNextItemWidth(ImGui::GetFrameHeight());
7311
0
                ImGui::DragInt("freeze_rows", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);
7312
0
                ImGui::TreePop();
7313
0
            }
7314
7315
0
            if (ImGui::TreeNodeEx("Sorting:", ImGuiTreeNodeFlags_DefaultOpen))
7316
0
            {
7317
0
                ImGui::CheckboxFlags("ImGuiTableFlags_SortMulti", &flags, ImGuiTableFlags_SortMulti);
7318
0
                ImGui::SameLine(); HelpMarker("When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).");
7319
0
                ImGui::CheckboxFlags("ImGuiTableFlags_SortTristate", &flags, ImGuiTableFlags_SortTristate);
7320
0
                ImGui::SameLine(); HelpMarker("When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).");
7321
0
                ImGui::TreePop();
7322
0
            }
7323
7324
0
            if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen))
7325
0
            {
7326
0
                ImGui::Checkbox("show_headers", &show_headers);
7327
0
                ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn);
7328
0
                ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader);
7329
0
                ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo).");
7330
0
                ImGui::TreePop();
7331
0
            }
7332
7333
0
            if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen))
7334
0
            {
7335
0
                ImGui::Checkbox("show_wrapped_text", &show_wrapped_text);
7336
7337
0
                ImGui::DragFloat2("##OuterSize", &outer_size_value.x);
7338
0
                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
7339
0
                ImGui::Checkbox("outer_size", &outer_size_enabled);
7340
0
                ImGui::SameLine();
7341
0
                HelpMarker("If scrolling is disabled (ScrollX and ScrollY not set):\n"
7342
0
                    "- The table is output directly in the parent window.\n"
7343
0
                    "- OuterSize.x < 0.0f will right-align the table.\n"
7344
0
                    "- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\n"
7345
0
                    "- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).");
7346
7347
                // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.
7348
                // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.
7349
0
                ImGui::DragFloat("inner_width (when ScrollX active)", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);
7350
7351
0
                ImGui::DragFloat("row_min_height", &row_min_height, 1.0f, 0.0f, FLT_MAX);
7352
0
                ImGui::SameLine(); HelpMarker("Specify height of the Selectable item.");
7353
7354
0
                ImGui::DragInt("items_count", &items_count, 0.1f, 0, 9999);
7355
0
                ImGui::Combo("items_type (first column)", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names));
7356
                //filter.Draw("filter");
7357
0
                ImGui::TreePop();
7358
0
            }
7359
7360
0
            ImGui::PopItemWidth();
7361
0
            PopStyleCompact();
7362
0
            ImGui::Spacing();
7363
0
            ImGui::TreePop();
7364
0
        }
7365
7366
        // Update item list if we changed the number of items
7367
0
        static ImVector<MyItem> items;
7368
0
        static ImVector<int> selection;
7369
0
        static bool items_need_sort = false;
7370
0
        if (items.Size != items_count)
7371
0
        {
7372
0
            items.resize(items_count, MyItem());
7373
0
            for (int n = 0; n < items_count; n++)
7374
0
            {
7375
0
                const int template_n = n % IM_ARRAYSIZE(template_items_names);
7376
0
                MyItem& item = items[n];
7377
0
                item.ID = n;
7378
0
                item.Name = template_items_names[template_n];
7379
0
                item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities
7380
0
            }
7381
0
        }
7382
7383
0
        const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();
7384
0
        const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;
7385
0
        ImVec2 table_scroll_cur, table_scroll_max; // For debug display
7386
0
        const ImDrawList* table_draw_list = NULL;  // "
7387
7388
        // Submit table
7389
0
        const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;
7390
0
        if (ImGui::BeginTable("table_advanced", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use))
7391
0
        {
7392
            // Declare columns
7393
            // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.
7394
            // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!
7395
0
            ImGui::TableSetupColumn("ID",           columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID);
7396
0
            ImGui::TableSetupColumn("Name",         columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);
7397
0
            ImGui::TableSetupColumn("Action",       columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);
7398
0
            ImGui::TableSetupColumn("Quantity",     columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity);
7399
0
            ImGui::TableSetupColumn("Description",  columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description);
7400
0
            ImGui::TableSetupColumn("Hidden",       columns_base_flags |  ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);
7401
0
            ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);
7402
7403
            // Sort our data if sort specs have been changed!
7404
0
            ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();
7405
0
            if (sort_specs && sort_specs->SpecsDirty)
7406
0
                items_need_sort = true;
7407
0
            if (sort_specs && items_need_sort && items.Size > 1)
7408
0
            {
7409
0
                MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);
7410
0
                sort_specs->SpecsDirty = false;
7411
0
            }
7412
0
            items_need_sort = false;
7413
7414
            // Take note of whether we are currently sorting based on the Quantity field,
7415
            // we will use this to trigger sorting when we know the data of this column has been modified.
7416
0
            const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;
7417
7418
            // Show headers
7419
0
            if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)
7420
0
                ImGui::TableAngledHeadersRow();
7421
0
            if (show_headers)
7422
0
                ImGui::TableHeadersRow();
7423
7424
            // Show data
7425
            // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?
7426
0
#if 1
7427
            // Demonstrate using clipper for large vertical lists
7428
0
            ImGuiListClipper clipper;
7429
0
            clipper.Begin(items.Size);
7430
0
            while (clipper.Step())
7431
0
            {
7432
0
                for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)
7433
#else
7434
            // Without clipper
7435
            {
7436
                for (int row_n = 0; row_n < items.Size; row_n++)
7437
#endif
7438
0
                {
7439
0
                    MyItem* item = &items[row_n];
7440
                    //if (!filter.PassFilter(item->Name))
7441
                    //    continue;
7442
7443
0
                    const bool item_is_selected = selection.contains(item->ID);
7444
0
                    ImGui::PushID(item->ID);
7445
0
                    ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height);
7446
7447
                    // For the demo purpose we can select among different type of items submitted in the first column
7448
0
                    ImGui::TableSetColumnIndex(0);
7449
0
                    char label[32];
7450
0
                    sprintf(label, "%04d", item->ID);
7451
0
                    if (contents_type == CT_Text)
7452
0
                        ImGui::TextUnformatted(label);
7453
0
                    else if (contents_type == CT_Button)
7454
0
                        ImGui::Button(label);
7455
0
                    else if (contents_type == CT_SmallButton)
7456
0
                        ImGui::SmallButton(label);
7457
0
                    else if (contents_type == CT_FillButton)
7458
0
                        ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));
7459
0
                    else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)
7460
0
                    {
7461
0
                        ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;
7462
0
                        if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height)))
7463
0
                        {
7464
0
                            if (ImGui::GetIO().KeyCtrl)
7465
0
                            {
7466
0
                                if (item_is_selected)
7467
0
                                    selection.find_erase_unsorted(item->ID);
7468
0
                                else
7469
0
                                    selection.push_back(item->ID);
7470
0
                            }
7471
0
                            else
7472
0
                            {
7473
0
                                selection.clear();
7474
0
                                selection.push_back(item->ID);
7475
0
                            }
7476
0
                        }
7477
0
                    }
7478
7479
0
                    if (ImGui::TableSetColumnIndex(1))
7480
0
                        ImGui::TextUnformatted(item->Name);
7481
7482
                    // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,
7483
                    // and we are currently sorting on the column showing the Quantity.
7484
                    // To avoid triggering a sort while holding the button, we only trigger it when the button has been released.
7485
                    // You will probably need some extra logic if you want to automatically sort when a specific entry changes.
7486
0
                    if (ImGui::TableSetColumnIndex(2))
7487
0
                    {
7488
0
                        if (ImGui::SmallButton("Chop")) { item->Quantity += 1; }
7489
0
                        if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7490
0
                        ImGui::SameLine();
7491
0
                        if (ImGui::SmallButton("Eat")) { item->Quantity -= 1; }
7492
0
                        if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }
7493
0
                    }
7494
7495
0
                    if (ImGui::TableSetColumnIndex(3))
7496
0
                        ImGui::Text("%d", item->Quantity);
7497
7498
0
                    ImGui::TableSetColumnIndex(4);
7499
0
                    if (show_wrapped_text)
7500
0
                        ImGui::TextWrapped("Lorem ipsum dolor sit amet");
7501
0
                    else
7502
0
                        ImGui::Text("Lorem ipsum dolor sit amet");
7503
7504
0
                    if (ImGui::TableSetColumnIndex(5))
7505
0
                        ImGui::Text("1234");
7506
7507
0
                    ImGui::PopID();
7508
0
                }
7509
0
            }
7510
7511
            // Store some info to display debug details below
7512
0
            table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
7513
0
            table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());
7514
0
            table_draw_list = ImGui::GetWindowDrawList();
7515
0
            ImGui::EndTable();
7516
0
        }
7517
0
        static bool show_debug_details = false;
7518
0
        ImGui::Checkbox("Debug details", &show_debug_details);
7519
0
        if (show_debug_details && table_draw_list)
7520
0
        {
7521
0
            ImGui::SameLine(0.0f, 0.0f);
7522
0
            const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;
7523
0
            if (table_draw_list == parent_draw_list)
7524
0
                ImGui::Text(": DrawCmd: +%d (in same window)",
7525
0
                    table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);
7526
0
            else
7527
0
                ImGui::Text(": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)",
7528
0
                    table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);
7529
0
        }
7530
0
        ImGui::TreePop();
7531
0
    }
7532
7533
0
    ImGui::PopID();
7534
7535
0
    DemoWindowColumns();
7536
7537
0
    if (disable_indent)
7538
0
        ImGui::PopStyleVar();
7539
0
}
7540
7541
// Demonstrate old/legacy Columns API!
7542
// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]
7543
static void DemoWindowColumns()
7544
0
{
7545
0
    IMGUI_DEMO_MARKER("Columns (legacy API)");
7546
0
    bool open = ImGui::TreeNode("Legacy Columns API");
7547
0
    ImGui::SameLine();
7548
0
    HelpMarker("Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!");
7549
0
    if (!open)
7550
0
        return;
7551
7552
    // Basic columns
7553
0
    IMGUI_DEMO_MARKER("Columns (legacy API)/Basic");
7554
0
    if (ImGui::TreeNode("Basic"))
7555
0
    {
7556
0
        ImGui::Text("Without border:");
7557
0
        ImGui::Columns(3, "mycolumns3", false);  // 3-ways, no border
7558
0
        ImGui::Separator();
7559
0
        for (int n = 0; n < 14; n++)
7560
0
        {
7561
0
            char label[32];
7562
0
            sprintf(label, "Item %d", n);
7563
0
            if (ImGui::Selectable(label)) {}
7564
            //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}
7565
0
            ImGui::NextColumn();
7566
0
        }
7567
0
        ImGui::Columns(1);
7568
0
        ImGui::Separator();
7569
7570
0
        ImGui::Text("With border:");
7571
0
        ImGui::Columns(4, "mycolumns"); // 4-ways, with border
7572
0
        ImGui::Separator();
7573
0
        ImGui::Text("ID"); ImGui::NextColumn();
7574
0
        ImGui::Text("Name"); ImGui::NextColumn();
7575
0
        ImGui::Text("Path"); ImGui::NextColumn();
7576
0
        ImGui::Text("Hovered"); ImGui::NextColumn();
7577
0
        ImGui::Separator();
7578
0
        const char* names[3] = { "One", "Two", "Three" };
7579
0
        const char* paths[3] = { "/path/one", "/path/two", "/path/three" };
7580
0
        static int selected = -1;
7581
0
        for (int i = 0; i < 3; i++)
7582
0
        {
7583
0
            char label[32];
7584
0
            sprintf(label, "%04d", i);
7585
0
            if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))
7586
0
                selected = i;
7587
0
            bool hovered = ImGui::IsItemHovered();
7588
0
            ImGui::NextColumn();
7589
0
            ImGui::Text(names[i]); ImGui::NextColumn();
7590
0
            ImGui::Text(paths[i]); ImGui::NextColumn();
7591
0
            ImGui::Text("%d", hovered); ImGui::NextColumn();
7592
0
        }
7593
0
        ImGui::Columns(1);
7594
0
        ImGui::Separator();
7595
0
        ImGui::TreePop();
7596
0
    }
7597
7598
0
    IMGUI_DEMO_MARKER("Columns (legacy API)/Borders");
7599
0
    if (ImGui::TreeNode("Borders"))
7600
0
    {
7601
        // NB: Future columns API should allow automatic horizontal borders.
7602
0
        static bool h_borders = true;
7603
0
        static bool v_borders = true;
7604
0
        static int columns_count = 4;
7605
0
        const int lines_count = 3;
7606
0
        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
7607
0
        ImGui::DragInt("##columns_count", &columns_count, 0.1f, 2, 10, "%d columns");
7608
0
        if (columns_count < 2)
7609
0
            columns_count = 2;
7610
0
        ImGui::SameLine();
7611
0
        ImGui::Checkbox("horizontal", &h_borders);
7612
0
        ImGui::SameLine();
7613
0
        ImGui::Checkbox("vertical", &v_borders);
7614
0
        ImGui::Columns(columns_count, NULL, v_borders);
7615
0
        for (int i = 0; i < columns_count * lines_count; i++)
7616
0
        {
7617
0
            if (h_borders && ImGui::GetColumnIndex() == 0)
7618
0
                ImGui::Separator();
7619
0
            ImGui::PushID(i);
7620
0
            ImGui::Text("%c%c%c", 'a' + i, 'a' + i, 'a' + i);
7621
0
            ImGui::Text("Width %.2f", ImGui::GetColumnWidth());
7622
0
            ImGui::Text("Avail %.2f", ImGui::GetContentRegionAvail().x);
7623
0
            ImGui::Text("Offset %.2f", ImGui::GetColumnOffset());
7624
0
            ImGui::Text("Long text that is likely to clip");
7625
0
            ImGui::Button("Button", ImVec2(-FLT_MIN, 0.0f));
7626
0
            ImGui::PopID();
7627
0
            ImGui::NextColumn();
7628
0
        }
7629
0
        ImGui::Columns(1);
7630
0
        if (h_borders)
7631
0
            ImGui::Separator();
7632
0
        ImGui::TreePop();
7633
0
    }
7634
7635
    // Create multiple items in a same cell before switching to next column
7636
0
    IMGUI_DEMO_MARKER("Columns (legacy API)/Mixed items");
7637
0
    if (ImGui::TreeNode("Mixed items"))
7638
0
    {
7639
0
        ImGui::Columns(3, "mixed");
7640
0
        ImGui::Separator();
7641
7642
0
        ImGui::Text("Hello");
7643
0
        ImGui::Button("Banana");
7644
0
        ImGui::NextColumn();
7645
7646
0
        ImGui::Text("ImGui");
7647
0
        ImGui::Button("Apple");
7648
0
        static float foo = 1.0f;
7649
0
        ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f");
7650
0
        ImGui::Text("An extra line here.");
7651
0
        ImGui::NextColumn();
7652
7653
0
        ImGui::Text("Sailor");
7654
0
        ImGui::Button("Corniflower");
7655
0
        static float bar = 1.0f;
7656
0
        ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f");
7657
0
        ImGui::NextColumn();
7658
7659
0
        if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7660
0
        if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7661
0
        if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn();
7662
0
        ImGui::Columns(1);
7663
0
        ImGui::Separator();
7664
0
        ImGui::TreePop();
7665
0
    }
7666
7667
    // Word wrapping
7668
0
    IMGUI_DEMO_MARKER("Columns (legacy API)/Word-wrapping");
7669
0
    if (ImGui::TreeNode("Word-wrapping"))
7670
0
    {
7671
0
        ImGui::Columns(2, "word-wrapping");
7672
0
        ImGui::Separator();
7673
0
        ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
7674
0
        ImGui::TextWrapped("Hello Left");
7675
0
        ImGui::NextColumn();
7676
0
        ImGui::TextWrapped("The quick brown fox jumps over the lazy dog.");
7677
0
        ImGui::TextWrapped("Hello Right");
7678
0
        ImGui::Columns(1);
7679
0
        ImGui::Separator();
7680
0
        ImGui::TreePop();
7681
0
    }
7682
7683
0
    IMGUI_DEMO_MARKER("Columns (legacy API)/Horizontal Scrolling");
7684
0
    if (ImGui::TreeNode("Horizontal Scrolling"))
7685
0
    {
7686
0
        ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));
7687
0
        ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);
7688
0
        ImGui::BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar);
7689
0
        ImGui::Columns(10);
7690
7691
        // Also demonstrate using clipper for large vertical lists
7692
0
        int ITEMS_COUNT = 2000;
7693
0
        ImGuiListClipper clipper;
7694
0
        clipper.Begin(ITEMS_COUNT);
7695
0
        while (clipper.Step())
7696
0
        {
7697
0
            for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
7698
0
                for (int j = 0; j < 10; j++)
7699
0
                {
7700
0
                    ImGui::Text("Line %d Column %d...", i, j);
7701
0
                    ImGui::NextColumn();
7702
0
                }
7703
0
        }
7704
0
        ImGui::Columns(1);
7705
0
        ImGui::EndChild();
7706
0
        ImGui::TreePop();
7707
0
    }
7708
7709
0
    IMGUI_DEMO_MARKER("Columns (legacy API)/Tree");
7710
0
    if (ImGui::TreeNode("Tree"))
7711
0
    {
7712
0
        ImGui::Columns(2, "tree", true);
7713
0
        for (int x = 0; x < 3; x++)
7714
0
        {
7715
0
            bool open1 = ImGui::TreeNode((void*)(intptr_t)x, "Node%d", x);
7716
0
            ImGui::NextColumn();
7717
0
            ImGui::Text("Node contents");
7718
0
            ImGui::NextColumn();
7719
0
            if (open1)
7720
0
            {
7721
0
                for (int y = 0; y < 3; y++)
7722
0
                {
7723
0
                    bool open2 = ImGui::TreeNode((void*)(intptr_t)y, "Node%d.%d", x, y);
7724
0
                    ImGui::NextColumn();
7725
0
                    ImGui::Text("Node contents");
7726
0
                    if (open2)
7727
0
                    {
7728
0
                        ImGui::Text("Even more contents");
7729
0
                        if (ImGui::TreeNode("Tree in column"))
7730
0
                        {
7731
0
                            ImGui::Text("The quick brown fox jumps over the lazy dog");
7732
0
                            ImGui::TreePop();
7733
0
                        }
7734
0
                    }
7735
0
                    ImGui::NextColumn();
7736
0
                    if (open2)
7737
0
                        ImGui::TreePop();
7738
0
                }
7739
0
                ImGui::TreePop();
7740
0
            }
7741
0
        }
7742
0
        ImGui::Columns(1);
7743
0
        ImGui::TreePop();
7744
0
    }
7745
7746
0
    ImGui::TreePop();
7747
0
}
7748
7749
//-----------------------------------------------------------------------------
7750
// [SECTION] DemoWindowInputs()
7751
//-----------------------------------------------------------------------------
7752
7753
static void DemoWindowInputs()
7754
0
{
7755
0
    IMGUI_DEMO_MARKER("Inputs & Focus");
7756
0
    if (ImGui::CollapsingHeader("Inputs & Focus"))
7757
0
    {
7758
0
        ImGuiIO& io = ImGui::GetIO();
7759
7760
        // Display inputs submitted to ImGuiIO
7761
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Inputs");
7762
0
        ImGui::SetNextItemOpen(true, ImGuiCond_Once);
7763
0
        bool inputs_opened = ImGui::TreeNode("Inputs");
7764
0
        ImGui::SameLine();
7765
0
        HelpMarker(
7766
0
            "This is a simplified view. See more detailed input state:\n"
7767
0
            "- in 'Tools->Metrics/Debugger->Inputs'.\n"
7768
0
            "- in 'Tools->Debug Log->IO'.");
7769
0
        if (inputs_opened)
7770
0
        {
7771
0
            if (ImGui::IsMousePosValid())
7772
0
                ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y);
7773
0
            else
7774
0
                ImGui::Text("Mouse pos: <INVALID>");
7775
0
            ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y);
7776
0
            ImGui::Text("Mouse down:");
7777
0
            for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); }
7778
0
            ImGui::Text("Mouse wheel: %.1f", io.MouseWheel);
7779
0
            ImGui::Text("Mouse clicked count:");
7780
0
            for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); }
7781
7782
            // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows
7783
            // displaying the data for old/new backends.
7784
            // User code should never have to go through such hoops!
7785
            // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.
7786
0
            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };
7787
0
            ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;
7788
0
            ImGui::Text("Keys down:");         for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); }
7789
0
            ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : "");
7790
0
            ImGui::Text("Chars queue:");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine();  ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.
7791
7792
0
            ImGui::TreePop();
7793
0
        }
7794
7795
        // Display ImGuiIO output flags
7796
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Outputs");
7797
0
        ImGui::SetNextItemOpen(true, ImGuiCond_Once);
7798
0
        bool outputs_opened = ImGui::TreeNode("Outputs");
7799
0
        ImGui::SameLine();
7800
0
        HelpMarker(
7801
0
            "The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui "
7802
0
            "to instruct your application of how to route inputs. Typically, when a value is true, it means "
7803
0
            "Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\n\n"
7804
0
            "The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, "
7805
0
            "and underlying application should ignore mouse inputs (in practice there are many and more subtle "
7806
0
            "rules leading to how those flags are set).");
7807
0
        if (outputs_opened)
7808
0
        {
7809
0
            ImGui::Text("io.WantCaptureMouse: %d", io.WantCaptureMouse);
7810
0
            ImGui::Text("io.WantCaptureMouseUnlessPopupClose: %d", io.WantCaptureMouseUnlessPopupClose);
7811
0
            ImGui::Text("io.WantCaptureKeyboard: %d", io.WantCaptureKeyboard);
7812
0
            ImGui::Text("io.WantTextInput: %d", io.WantTextInput);
7813
0
            ImGui::Text("io.WantSetMousePos: %d", io.WantSetMousePos);
7814
0
            ImGui::Text("io.NavActive: %d, io.NavVisible: %d", io.NavActive, io.NavVisible);
7815
7816
0
            IMGUI_DEMO_MARKER("Inputs & Focus/Outputs/WantCapture override");
7817
0
            if (ImGui::TreeNode("WantCapture override"))
7818
0
            {
7819
0
                HelpMarker(
7820
0
                    "Hovering the colored canvas will override io.WantCaptureXXX fields.\n"
7821
0
                    "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering "
7822
0
                    "and true when clicking.");
7823
0
                static int capture_override_mouse = -1;
7824
0
                static int capture_override_keyboard = -1;
7825
0
                const char* capture_override_desc[] = { "None", "Set to false", "Set to true" };
7826
0
                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7827
0
                ImGui::SliderInt("SetNextFrameWantCaptureMouse() on hover", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp);
7828
0
                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);
7829
0
                ImGui::SliderInt("SetNextFrameWantCaptureKeyboard() on hover", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp);
7830
7831
0
                ImGui::ColorButton("##panel", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item
7832
0
                if (ImGui::IsItemHovered() && capture_override_mouse != -1)
7833
0
                    ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);
7834
0
                if (ImGui::IsItemHovered() && capture_override_keyboard != -1)
7835
0
                    ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);
7836
7837
0
                ImGui::TreePop();
7838
0
            }
7839
0
            ImGui::TreePop();
7840
0
        }
7841
7842
        // Demonstrate using Shortcut() and Routing Policies.
7843
        // The general flow is:
7844
        // - Code interested in a chord (e.g. "Ctrl+A") declares their intent.
7845
        // - Multiple locations may be interested in same chord! Routing helps find a winner.
7846
        // - Every frame, we resolve all claims and assign one owner if the modifiers are matching.
7847
        // - The lower-level function is 'bool SetShortcutRouting()', returns true when caller got the route.
7848
        // - Most of the times, SetShortcutRouting() is not called directly. User mostly calls Shortcut() with routing flags.
7849
        // - If you call Shortcut() WITHOUT any routing option, it uses ImGuiInputFlags_RouteFocused.
7850
        // TL;DR: Most uses will simply be:
7851
        // - Shortcut(ImGuiMod_Ctrl | ImGuiKey_A); // Use ImGuiInputFlags_RouteFocused policy.
7852
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Shortcuts");
7853
0
        if (ImGui::TreeNode("Shortcuts"))
7854
0
        {
7855
0
            static ImGuiInputFlags route_options = ImGuiInputFlags_Repeat;
7856
0
            static ImGuiInputFlags route_type = ImGuiInputFlags_RouteFocused;
7857
0
            ImGui::CheckboxFlags("ImGuiInputFlags_Repeat", &route_options, ImGuiInputFlags_Repeat);
7858
0
            ImGui::RadioButton("ImGuiInputFlags_RouteActive", &route_type, ImGuiInputFlags_RouteActive);
7859
0
            ImGui::RadioButton("ImGuiInputFlags_RouteFocused (default)", &route_type, ImGuiInputFlags_RouteFocused);
7860
0
            ImGui::RadioButton("ImGuiInputFlags_RouteGlobal", &route_type, ImGuiInputFlags_RouteGlobal);
7861
0
            ImGui::Indent();
7862
0
            ImGui::BeginDisabled(route_type != ImGuiInputFlags_RouteGlobal);
7863
0
            ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverFocused", &route_options, ImGuiInputFlags_RouteOverFocused);
7864
0
            ImGui::CheckboxFlags("ImGuiInputFlags_RouteOverActive", &route_options, ImGuiInputFlags_RouteOverActive);
7865
0
            ImGui::CheckboxFlags("ImGuiInputFlags_RouteUnlessBgFocused", &route_options, ImGuiInputFlags_RouteUnlessBgFocused);
7866
0
            ImGui::EndDisabled();
7867
0
            ImGui::Unindent();
7868
0
            ImGui::RadioButton("ImGuiInputFlags_RouteAlways", &route_type, ImGuiInputFlags_RouteAlways);
7869
0
            ImGuiInputFlags flags = route_type | route_options; // Merged flags
7870
0
            if (route_type != ImGuiInputFlags_RouteGlobal)
7871
0
                flags &= ~(ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused);
7872
7873
0
            ImGui::SeparatorText("Using SetNextItemShortcut()");
7874
0
            ImGui::Text("Ctrl+S");
7875
0
            ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, flags | ImGuiInputFlags_Tooltip);
7876
0
            ImGui::Button("Save");
7877
0
            ImGui::Text("Alt+F");
7878
0
            ImGui::SetNextItemShortcut(ImGuiMod_Alt | ImGuiKey_F, flags | ImGuiInputFlags_Tooltip);
7879
0
            static float f = 0.5f;
7880
0
            ImGui::SliderFloat("Factor", &f, 0.0f, 1.0f);
7881
7882
0
            ImGui::SeparatorText("Using Shortcut()");
7883
0
            const float line_height = ImGui::GetTextLineHeightWithSpacing();
7884
0
            const ImGuiKeyChord key_chord = ImGuiMod_Ctrl | ImGuiKey_A;
7885
7886
0
            ImGui::Text("Ctrl+A");
7887
0
            ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7888
7889
0
            ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 0.0f, 1.0f, 0.1f));
7890
7891
0
            ImGui::BeginChild("WindowA", ImVec2(-FLT_MIN, line_height * 14), true);
7892
0
            ImGui::Text("Press CTRL+A and see who receives it!");
7893
0
            ImGui::Separator();
7894
7895
            // 1: Window polling for CTRL+A
7896
0
            ImGui::Text("(in WindowA)");
7897
0
            ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7898
7899
            // 2: InputText also polling for CTRL+A: it always uses _RouteFocused internally (gets priority when active)
7900
            // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7901
            //char str[16] = "Press CTRL+A";
7902
            //ImGui::Spacing();
7903
            //ImGui::InputText("InputTextB", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7904
            //ImGuiID item_id = ImGui::GetItemID();
7905
            //ImGui::SameLine(); HelpMarker("Internal widgets always use _RouteFocused");
7906
            //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, item_id) ? "PRESSED" : "...");
7907
7908
            // 3: Dummy child is not claiming the route: focusing them shouldn't steal route away from WindowA
7909
0
            ImGui::BeginChild("ChildD", ImVec2(-FLT_MIN, line_height * 4), true);
7910
0
            ImGui::Text("(in ChildD: not using same Shortcut)");
7911
0
            ImGui::Text("IsWindowFocused: %d", ImGui::IsWindowFocused());
7912
0
            ImGui::EndChild();
7913
7914
            // 4: Child window polling for CTRL+A. It is deeper than WindowA and gets priority when focused.
7915
0
            ImGui::BeginChild("ChildE", ImVec2(-FLT_MIN, line_height * 4), true);
7916
0
            ImGui::Text("(in ChildE: using same Shortcut)");
7917
0
            ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7918
0
            ImGui::EndChild();
7919
7920
            // 5: In a popup
7921
0
            if (ImGui::Button("Open Popup"))
7922
0
                ImGui::OpenPopup("PopupF");
7923
0
            if (ImGui::BeginPopup("PopupF"))
7924
0
            {
7925
0
                ImGui::Text("(in PopupF)");
7926
0
                ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags) ? "PRESSED" : "...");
7927
                // (Commented because the owner-aware version of Shortcut() is still in imgui_internal.h)
7928
                //ImGui::InputText("InputTextG", str, IM_ARRAYSIZE(str), ImGuiInputTextFlags_ReadOnly);
7929
                //ImGui::Text("IsWindowFocused: %d, Shortcut: %s", ImGui::IsWindowFocused(), ImGui::Shortcut(key_chord, flags, ImGui::GetItemID()) ? "PRESSED" : "...");
7930
0
                ImGui::EndPopup();
7931
0
            }
7932
0
            ImGui::EndChild();
7933
0
            ImGui::PopStyleColor();
7934
7935
0
            ImGui::TreePop();
7936
0
        }
7937
7938
        // Display mouse cursors
7939
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Mouse Cursors");
7940
0
        if (ImGui::TreeNode("Mouse Cursors"))
7941
0
        {
7942
0
            const char* mouse_cursors_names[] = { "Arrow", "TextInput", "ResizeAll", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE", "Hand", "Wait", "Progress", "NotAllowed" };
7943
0
            IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);
7944
7945
0
            ImGuiMouseCursor current = ImGui::GetMouseCursor();
7946
0
            const char* cursor_name = (current >= ImGuiMouseCursor_Arrow) && (current < ImGuiMouseCursor_COUNT) ? mouse_cursors_names[current] : "N/A";
7947
0
            ImGui::Text("Current mouse cursor = %d: %s", current, cursor_name);
7948
0
            ImGui::BeginDisabled(true);
7949
0
            ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);
7950
0
            ImGui::EndDisabled();
7951
7952
0
            ImGui::Text("Hover to see mouse cursors:");
7953
0
            ImGui::SameLine(); HelpMarker(
7954
0
                "Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. "
7955
0
                "If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, "
7956
0
                "otherwise your backend needs to handle it.");
7957
0
            for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)
7958
0
            {
7959
0
                char label[32];
7960
0
                sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]);
7961
0
                ImGui::Bullet(); ImGui::Selectable(label, false);
7962
0
                if (ImGui::IsItemHovered())
7963
0
                    ImGui::SetMouseCursor(i);
7964
0
            }
7965
0
            ImGui::TreePop();
7966
0
        }
7967
7968
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Tabbing");
7969
0
        if (ImGui::TreeNode("Tabbing"))
7970
0
        {
7971
0
            ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields.");
7972
0
            static char buf[32] = "hello";
7973
0
            ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
7974
0
            ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
7975
0
            ImGui::InputText("3", buf, IM_ARRAYSIZE(buf));
7976
0
            ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
7977
0
            ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf));
7978
0
            ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
7979
0
            ImGui::PopItemFlag();
7980
0
            ImGui::InputText("5", buf, IM_ARRAYSIZE(buf));
7981
0
            ImGui::TreePop();
7982
0
        }
7983
7984
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Focus from code");
7985
0
        if (ImGui::TreeNode("Focus from code"))
7986
0
        {
7987
0
            bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine();
7988
0
            bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine();
7989
0
            bool focus_3 = ImGui::Button("Focus on 3");
7990
0
            int has_focus = 0;
7991
0
            static char buf[128] = "click on a button to set focus";
7992
7993
0
            if (focus_1) ImGui::SetKeyboardFocusHere();
7994
0
            ImGui::InputText("1", buf, IM_ARRAYSIZE(buf));
7995
0
            if (ImGui::IsItemActive()) has_focus = 1;
7996
7997
0
            if (focus_2) ImGui::SetKeyboardFocusHere();
7998
0
            ImGui::InputText("2", buf, IM_ARRAYSIZE(buf));
7999
0
            if (ImGui::IsItemActive()) has_focus = 2;
8000
8001
0
            ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, true);
8002
0
            if (focus_3) ImGui::SetKeyboardFocusHere();
8003
0
            ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf));
8004
0
            if (ImGui::IsItemActive()) has_focus = 3;
8005
0
            ImGui::SameLine(); HelpMarker("Item won't be cycled through when using TAB or Shift+Tab.");
8006
0
            ImGui::PopItemFlag();
8007
8008
0
            if (has_focus)
8009
0
                ImGui::Text("Item with focus: %d", has_focus);
8010
0
            else
8011
0
                ImGui::Text("Item with focus: <none>");
8012
8013
            // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item
8014
0
            static float f3[3] = { 0.0f, 0.0f, 0.0f };
8015
0
            int focus_ahead = -1;
8016
0
            if (ImGui::Button("Focus on X")) { focus_ahead = 0; } ImGui::SameLine();
8017
0
            if (ImGui::Button("Focus on Y")) { focus_ahead = 1; } ImGui::SameLine();
8018
0
            if (ImGui::Button("Focus on Z")) { focus_ahead = 2; }
8019
0
            if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);
8020
0
            ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f);
8021
8022
0
            ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code.");
8023
0
            ImGui::TreePop();
8024
0
        }
8025
8026
0
        IMGUI_DEMO_MARKER("Inputs & Focus/Dragging");
8027
0
        if (ImGui::TreeNode("Dragging"))
8028
0
        {
8029
0
            ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");
8030
0
            for (int button = 0; button < 3; button++)
8031
0
            {
8032
0
                ImGui::Text("IsMouseDragging(%d):", button);
8033
0
                ImGui::Text("  w/ default threshold: %d,", ImGui::IsMouseDragging(button));
8034
0
                ImGui::Text("  w/ zero threshold: %d,", ImGui::IsMouseDragging(button, 0.0f));
8035
0
                ImGui::Text("  w/ large threshold: %d,", ImGui::IsMouseDragging(button, 20.0f));
8036
0
            }
8037
8038
0
            ImGui::Button("Drag Me");
8039
0
            if (ImGui::IsItemActive())
8040
0
                ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor
8041
8042
            // Drag operations gets "unlocked" when the mouse has moved past a certain threshold
8043
            // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher
8044
            // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().
8045
0
            ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);
8046
0
            ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);
8047
0
            ImVec2 mouse_delta = io.MouseDelta;
8048
0
            ImGui::Text("GetMouseDragDelta(0):");
8049
0
            ImGui::Text("  w/ default threshold: (%.1f, %.1f)", value_with_lock_threshold.x, value_with_lock_threshold.y);
8050
0
            ImGui::Text("  w/ zero threshold: (%.1f, %.1f)", value_raw.x, value_raw.y);
8051
0
            ImGui::Text("io.MouseDelta: (%.1f, %.1f)", mouse_delta.x, mouse_delta.y);
8052
0
            ImGui::TreePop();
8053
0
        }
8054
0
    }
8055
0
}
8056
8057
//-----------------------------------------------------------------------------
8058
// [SECTION] About Window / ShowAboutWindow()
8059
// Access from Dear ImGui Demo -> Tools -> About
8060
//-----------------------------------------------------------------------------
8061
8062
void ImGui::ShowAboutWindow(bool* p_open)
8063
0
{
8064
0
    if (!ImGui::Begin("About Dear ImGui", p_open, ImGuiWindowFlags_AlwaysAutoResize))
8065
0
    {
8066
0
        ImGui::End();
8067
0
        return;
8068
0
    }
8069
0
    IMGUI_DEMO_MARKER("Tools/About Dear ImGui");
8070
0
    ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8071
8072
0
    ImGui::TextLinkOpenURL("Homepage", "https://github.com/ocornut/imgui");
8073
0
    ImGui::SameLine();
8074
0
    ImGui::TextLinkOpenURL("FAQ", "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md");
8075
0
    ImGui::SameLine();
8076
0
    ImGui::TextLinkOpenURL("Wiki", "https://github.com/ocornut/imgui/wiki");
8077
0
    ImGui::SameLine();
8078
0
    ImGui::TextLinkOpenURL("Extensions", "https://github.com/ocornut/imgui/wiki/Useful-Extensions");
8079
0
    ImGui::SameLine();
8080
0
    ImGui::TextLinkOpenURL("Releases", "https://github.com/ocornut/imgui/releases");
8081
0
    ImGui::SameLine();
8082
0
    ImGui::TextLinkOpenURL("Funding", "https://github.com/ocornut/imgui/wiki/Funding");
8083
8084
0
    ImGui::Separator();
8085
0
    ImGui::Text("(c) 2014-2025 Omar Cornut");
8086
0
    ImGui::Text("Developed by Omar Cornut and all Dear ImGui contributors.");
8087
0
    ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information.");
8088
0
    ImGui::Text("If your company uses this, please consider funding the project.");
8089
8090
0
    static bool show_config_info = false;
8091
0
    ImGui::Checkbox("Config/Build Information", &show_config_info);
8092
0
    if (show_config_info)
8093
0
    {
8094
0
        ImGuiIO& io = ImGui::GetIO();
8095
0
        ImGuiStyle& style = ImGui::GetStyle();
8096
8097
0
        bool copy_to_clipboard = ImGui::Button("Copy to clipboard");
8098
0
        ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);
8099
0
        ImGui::BeginChild(ImGui::GetID("cfg_infos"), child_size, ImGuiChildFlags_FrameStyle);
8100
0
        if (copy_to_clipboard)
8101
0
        {
8102
0
            ImGui::LogToClipboard();
8103
0
            ImGui::LogText("```cpp\n"); // Back quotes will make text appears without formatting when pasting on GitHub
8104
0
        }
8105
8106
0
        ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM);
8107
0
        ImGui::Separator();
8108
0
        ImGui::Text("sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));
8109
0
        ImGui::Text("define: __cplusplus=%d", (int)__cplusplus);
8110
#ifdef IMGUI_ENABLE_TEST_ENGINE
8111
        ImGui::Text("define: IMGUI_ENABLE_TEST_ENGINE");
8112
#endif
8113
#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
8114
        ImGui::Text("define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS");
8115
#endif
8116
#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS
8117
        ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS");
8118
#endif
8119
#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS
8120
        ImGui::Text("define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS");
8121
#endif
8122
#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS
8123
        ImGui::Text("define: IMGUI_DISABLE_WIN32_FUNCTIONS");
8124
#endif
8125
#ifdef IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS
8126
        ImGui::Text("define: IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS");
8127
#endif
8128
#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
8129
        ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS");
8130
#endif
8131
#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
8132
        ImGui::Text("define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS");
8133
#endif
8134
#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
8135
        ImGui::Text("define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS");
8136
#endif
8137
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
8138
        ImGui::Text("define: IMGUI_DISABLE_FILE_FUNCTIONS");
8139
#endif
8140
#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS
8141
        ImGui::Text("define: IMGUI_DISABLE_DEFAULT_ALLOCATORS");
8142
#endif
8143
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
8144
        ImGui::Text("define: IMGUI_USE_BGRA_PACKED_COLOR");
8145
#endif
8146
#ifdef _WIN32
8147
        ImGui::Text("define: _WIN32");
8148
#endif
8149
#ifdef _WIN64
8150
        ImGui::Text("define: _WIN64");
8151
#endif
8152
#ifdef __linux__
8153
        ImGui::Text("define: __linux__");
8154
#endif
8155
0
#ifdef __APPLE__
8156
0
        ImGui::Text("define: __APPLE__");
8157
0
#endif
8158
#ifdef _MSC_VER
8159
        ImGui::Text("define: _MSC_VER=%d", _MSC_VER);
8160
#endif
8161
#ifdef _MSVC_LANG
8162
        ImGui::Text("define: _MSVC_LANG=%d", (int)_MSVC_LANG);
8163
#endif
8164
#ifdef __MINGW32__
8165
        ImGui::Text("define: __MINGW32__");
8166
#endif
8167
#ifdef __MINGW64__
8168
        ImGui::Text("define: __MINGW64__");
8169
#endif
8170
0
#ifdef __GNUC__
8171
0
        ImGui::Text("define: __GNUC__=%d", (int)__GNUC__);
8172
0
#endif
8173
0
#ifdef __clang_version__
8174
0
        ImGui::Text("define: __clang_version__=%s", __clang_version__);
8175
0
#endif
8176
#ifdef __EMSCRIPTEN__
8177
        ImGui::Text("define: __EMSCRIPTEN__");
8178
        ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
8179
#endif
8180
0
        ImGui::Separator();
8181
0
        ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL");
8182
0
        ImGui::Text("io.BackendRendererName: %s", io.BackendRendererName ? io.BackendRendererName : "NULL");
8183
0
        ImGui::Text("io.ConfigFlags: 0x%08X", io.ConfigFlags);
8184
0
        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)        ImGui::Text(" NavEnableKeyboard");
8185
0
        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)         ImGui::Text(" NavEnableGamepad");
8186
0
        if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)                  ImGui::Text(" NoMouse");
8187
0
        if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)      ImGui::Text(" NoMouseCursorChange");
8188
0
        if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard)               ImGui::Text(" NoKeyboard");
8189
0
        if (io.MouseDrawCursor)                                         ImGui::Text("io.MouseDrawCursor");
8190
0
        if (io.ConfigMacOSXBehaviors)                                   ImGui::Text("io.ConfigMacOSXBehaviors");
8191
0
        if (io.ConfigNavMoveSetMousePos)                                ImGui::Text("io.ConfigNavMoveSetMousePos");
8192
0
        if (io.ConfigNavCaptureKeyboard)                                ImGui::Text("io.ConfigNavCaptureKeyboard");
8193
0
        if (io.ConfigInputTextCursorBlink)                              ImGui::Text("io.ConfigInputTextCursorBlink");
8194
0
        if (io.ConfigWindowsResizeFromEdges)                            ImGui::Text("io.ConfigWindowsResizeFromEdges");
8195
0
        if (io.ConfigWindowsMoveFromTitleBarOnly)                       ImGui::Text("io.ConfigWindowsMoveFromTitleBarOnly");
8196
0
        if (io.ConfigMemoryCompactTimer >= 0.0f)                        ImGui::Text("io.ConfigMemoryCompactTimer = %.1f", io.ConfigMemoryCompactTimer);
8197
0
        ImGui::Text("io.BackendFlags: 0x%08X", io.BackendFlags);
8198
0
        if (io.BackendFlags & ImGuiBackendFlags_HasGamepad)             ImGui::Text(" HasGamepad");
8199
0
        if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)        ImGui::Text(" HasMouseCursors");
8200
0
        if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)         ImGui::Text(" HasSetMousePos");
8201
0
        if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)   ImGui::Text(" RendererHasVtxOffset");
8202
0
        if (io.BackendFlags & ImGuiBackendFlags_RendererHasTextures)    ImGui::Text(" RendererHasTextures");
8203
0
        ImGui::Separator();
8204
0
        ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexData->Width, io.Fonts->TexData->Height);
8205
0
        ImGui::Text("io.Fonts->FontLoaderName: %s", io.Fonts->FontLoaderName ? io.Fonts->FontLoaderName : "NULL");
8206
0
        ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y);
8207
0
        ImGui::Text("io.DisplayFramebufferScale: %.2f,%.2f", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
8208
0
        ImGui::Separator();
8209
0
        ImGui::Text("style.WindowPadding: %.2f,%.2f", style.WindowPadding.x, style.WindowPadding.y);
8210
0
        ImGui::Text("style.WindowBorderSize: %.2f", style.WindowBorderSize);
8211
0
        ImGui::Text("style.FramePadding: %.2f,%.2f", style.FramePadding.x, style.FramePadding.y);
8212
0
        ImGui::Text("style.FrameRounding: %.2f", style.FrameRounding);
8213
0
        ImGui::Text("style.FrameBorderSize: %.2f", style.FrameBorderSize);
8214
0
        ImGui::Text("style.ItemSpacing: %.2f,%.2f", style.ItemSpacing.x, style.ItemSpacing.y);
8215
0
        ImGui::Text("style.ItemInnerSpacing: %.2f,%.2f", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);
8216
8217
0
        if (copy_to_clipboard)
8218
0
        {
8219
0
            ImGui::LogText("\n```\n");
8220
0
            ImGui::LogFinish();
8221
0
        }
8222
0
        ImGui::EndChild();
8223
0
    }
8224
0
    ImGui::End();
8225
0
}
8226
8227
//-----------------------------------------------------------------------------
8228
// [SECTION] Style Editor / ShowStyleEditor()
8229
//-----------------------------------------------------------------------------
8230
// - ShowStyleSelector()
8231
// - ShowStyleEditor()
8232
//-----------------------------------------------------------------------------
8233
8234
// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.
8235
bool ImGui::ShowStyleSelector(const char* label)
8236
0
{
8237
    // FIXME: This is a bit tricky to get right as style are functions, they don't register a name nor the fact that one is active.
8238
    // So we keep track of last active one among our limited selection.
8239
0
    static int style_idx = -1;
8240
0
    const char* style_names[] = { "Dark", "Light", "Classic" };
8241
0
    bool ret = false;
8242
0
    if (ImGui::BeginCombo(label, (style_idx >= 0 && style_idx < IM_ARRAYSIZE(style_names)) ? style_names[style_idx] : ""))
8243
0
    {
8244
0
        for (int n = 0; n < IM_ARRAYSIZE(style_names); n++)
8245
0
        {
8246
0
            if (ImGui::Selectable(style_names[n], style_idx == n, ImGuiSelectableFlags_SelectOnNav))
8247
0
            {
8248
0
                style_idx = n;
8249
0
                ret = true;
8250
0
                switch (style_idx)
8251
0
                {
8252
0
                case 0: ImGui::StyleColorsDark(); break;
8253
0
                case 1: ImGui::StyleColorsLight(); break;
8254
0
                case 2: ImGui::StyleColorsClassic(); break;
8255
0
                }
8256
0
            }
8257
0
            else if (style_idx == n)
8258
0
                ImGui::SetItemDefaultFocus();
8259
0
        }
8260
0
        ImGui::EndCombo();
8261
0
    }
8262
0
    return ret;
8263
0
}
8264
8265
static const char* GetTreeLinesFlagsName(ImGuiTreeNodeFlags flags)
8266
0
{
8267
0
    if (flags == ImGuiTreeNodeFlags_DrawLinesNone) return "DrawLinesNone";
8268
0
    if (flags == ImGuiTreeNodeFlags_DrawLinesFull) return "DrawLinesFull";
8269
0
    if (flags == ImGuiTreeNodeFlags_DrawLinesToNodes) return "DrawLinesToNodes";
8270
0
    return "";
8271
0
}
8272
8273
// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8274
void ImGui::ShowStyleEditor(ImGuiStyle* ref)
8275
0
{
8276
0
    IMGUI_DEMO_MARKER("Tools/Style Editor");
8277
    // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to
8278
    // (without a reference style pointer, we will use one compared locally as a reference)
8279
0
    ImGuiStyle& style = GetStyle();
8280
0
    static ImGuiStyle ref_saved_style;
8281
8282
    // Default to using internal storage as reference
8283
0
    static bool init = true;
8284
0
    if (init && ref == NULL)
8285
0
        ref_saved_style = style;
8286
0
    init = false;
8287
0
    if (ref == NULL)
8288
0
        ref = &ref_saved_style;
8289
8290
0
    PushItemWidth(GetWindowWidth() * 0.50f);
8291
8292
0
    {
8293
        // General
8294
0
        SeparatorText("General");
8295
0
        if ((GetIO().BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
8296
0
        {
8297
0
            BulletText("Warning: Font scaling will NOT be smooth, because\nImGuiBackendFlags_RendererHasTextures is not set!");
8298
0
            BulletText("For instructions, see:");
8299
0
            SameLine();
8300
0
            TextLinkOpenURL("docs/BACKENDS.md", "https://github.com/ocornut/imgui/blob/master/docs/BACKENDS.md");
8301
0
        }
8302
8303
0
        if (ShowStyleSelector("Colors##Selector"))
8304
0
            ref_saved_style = style;
8305
0
        ShowFontSelector("Fonts##Selector");
8306
0
        if (DragFloat("FontSizeBase", &style.FontSizeBase, 0.20f, 5.0f, 100.0f, "%.0f"))
8307
0
            style._NextFrameFontSizeBase = style.FontSizeBase; // FIXME: Temporary hack until we finish remaining work.
8308
0
        SameLine(0.0f, 0.0f); Text(" (out %.2f)", GetFontSize());
8309
0
        DragFloat("FontScaleMain", &style.FontScaleMain, 0.02f, 0.5f, 4.0f);
8310
        //BeginDisabled(GetIO().ConfigDpiScaleFonts);
8311
0
        DragFloat("FontScaleDpi", &style.FontScaleDpi, 0.02f, 0.5f, 4.0f);
8312
        //SetItemTooltip("When io.ConfigDpiScaleFonts is set, this value is automatically overwritten.");
8313
        //EndDisabled();
8314
8315
        // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)
8316
0
        if (SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"))
8317
0
            style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding
8318
0
        { bool border = (style.WindowBorderSize > 0.0f); if (Checkbox("WindowBorder", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }
8319
0
        SameLine();
8320
0
        { bool border = (style.FrameBorderSize > 0.0f);  if (Checkbox("FrameBorder", &border)) { style.FrameBorderSize = border ? 1.0f : 0.0f; } }
8321
0
        SameLine();
8322
0
        { bool border = (style.PopupBorderSize > 0.0f);  if (Checkbox("PopupBorder", &border)) { style.PopupBorderSize = border ? 1.0f : 0.0f; } }
8323
0
    }
8324
8325
    // Save/Revert button
8326
0
    if (Button("Save Ref"))
8327
0
        *ref = ref_saved_style = style;
8328
0
    SameLine();
8329
0
    if (Button("Revert Ref"))
8330
0
        style = *ref;
8331
0
    SameLine();
8332
0
    HelpMarker(
8333
0
        "Save/Revert in local non-persistent storage. Default Colors definition are not affected. "
8334
0
        "Use \"Export\" below to save them somewhere.");
8335
8336
0
    SeparatorText("Details");
8337
0
    if (BeginTabBar("##tabs", ImGuiTabBarFlags_None))
8338
0
    {
8339
0
        if (BeginTabItem("Sizes"))
8340
0
        {
8341
0
            SeparatorText("Main");
8342
0
            SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f");
8343
0
            SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f");
8344
0
            SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f");
8345
0
            SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f");
8346
0
            SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f");
8347
0
            SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f");
8348
0
            SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f");
8349
8350
0
            SeparatorText("Borders");
8351
0
            SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f");
8352
0
            SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f");
8353
0
            SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f");
8354
0
            SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f");
8355
8356
0
            SeparatorText("Rounding");
8357
0
            SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
8358
0
            SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
8359
0
            SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
8360
0
            SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
8361
0
            SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
8362
8363
0
            SeparatorText("Scrollbar");
8364
0
            SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f");
8365
0
            SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
8366
0
            SliderFloat("ScrollbarPadding", &style.ScrollbarPadding, 0.0f, 10.0f, "%.0f");
8367
8368
0
            SeparatorText("Tabs");
8369
0
            SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f");
8370
0
            SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f");
8371
0
            SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f");
8372
0
            SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set.");
8373
0
            DragFloat("TabMinWidthBase", &style.TabMinWidthBase, 0.5f, 1.0f, 500.0f, "%.0f");
8374
0
            DragFloat("TabMinWidthShrink", &style.TabMinWidthShrink, 0.5f, 1.0f, 500.0f, "%0.f");
8375
0
            DragFloat("TabCloseButtonMinWidthSelected", &style.TabCloseButtonMinWidthSelected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthSelected < 0.0f) ? "%.0f (Always)" : "%.0f");
8376
0
            DragFloat("TabCloseButtonMinWidthUnselected", &style.TabCloseButtonMinWidthUnselected, 0.5f, -1.0f, 100.0f, (style.TabCloseButtonMinWidthUnselected < 0.0f) ? "%.0f (Always)" : "%.0f");
8377
0
            SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f");
8378
8379
0
            SeparatorText("Tables");
8380
0
            SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f");
8381
0
            SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f);
8382
0
            SliderFloat2("TableAngledHeadersTextAlign", (float*)&style.TableAngledHeadersTextAlign, 0.0f, 1.0f, "%.2f");
8383
8384
0
            SeparatorText("Trees");
8385
0
            bool combo_open = BeginCombo("TreeLinesFlags", GetTreeLinesFlagsName(style.TreeLinesFlags));
8386
0
            SameLine();
8387
0
            HelpMarker("[Experimental] Tree lines may not work in all situations (e.g. using a clipper) and may incurs slight traversal overhead.\n\nImGuiTreeNodeFlags_DrawLinesFull is faster than ImGuiTreeNodeFlags_DrawLinesToNode.");
8388
0
            if (combo_open)
8389
0
            {
8390
0
                const ImGuiTreeNodeFlags options[] = { ImGuiTreeNodeFlags_DrawLinesNone, ImGuiTreeNodeFlags_DrawLinesFull, ImGuiTreeNodeFlags_DrawLinesToNodes };
8391
0
                for (ImGuiTreeNodeFlags option : options)
8392
0
                    if (Selectable(GetTreeLinesFlagsName(option), style.TreeLinesFlags == option))
8393
0
                        style.TreeLinesFlags = option;
8394
0
                EndCombo();
8395
0
            }
8396
0
            SliderFloat("TreeLinesSize", &style.TreeLinesSize, 0.0f, 2.0f, "%.0f");
8397
0
            SliderFloat("TreeLinesRounding", &style.TreeLinesRounding, 0.0f, 12.0f, "%.0f");
8398
8399
0
            SeparatorText("Windows");
8400
0
            SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f");
8401
0
            SliderFloat("WindowBorderHoverPadding", &style.WindowBorderHoverPadding, 1.0f, 20.0f, "%.0f");
8402
0
            int window_menu_button_position = style.WindowMenuButtonPosition + 1;
8403
0
            if (Combo("WindowMenuButtonPosition", (int*)&window_menu_button_position, "None\0Left\0Right\0"))
8404
0
                style.WindowMenuButtonPosition = (ImGuiDir)(window_menu_button_position - 1);
8405
8406
0
            SeparatorText("Widgets");
8407
0
            Combo("ColorButtonPosition", (int*)&style.ColorButtonPosition, "Left\0Right\0");
8408
0
            SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f");
8409
0
            SameLine(); HelpMarker("Alignment applies when a button is larger than its text content.");
8410
0
            SliderFloat2("SelectableTextAlign", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, "%.2f");
8411
0
            SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content.");
8412
0
            SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f");
8413
0
            SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f");
8414
0
            SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f");
8415
0
            SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f");
8416
0
            SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, 1.0f, "%.0f");
8417
8418
0
            SeparatorText("Tooltips");
8419
0
            for (int n = 0; n < 2; n++)
8420
0
                if (TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav"))
8421
0
                {
8422
0
                    ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;
8423
0
                    CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone);
8424
0
                    CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort);
8425
0
                    CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal);
8426
0
                    CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary);
8427
0
                    CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay);
8428
0
                    TreePop();
8429
0
                }
8430
8431
0
            SeparatorText("Misc");
8432
0
            SliderFloat2("DisplayWindowPadding", (float*)&style.DisplayWindowPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen.");
8433
0
            SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); SameLine(); HelpMarker("Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).");
8434
8435
0
            EndTabItem();
8436
0
        }
8437
8438
0
        if (BeginTabItem("Colors"))
8439
0
        {
8440
0
            static int output_dest = 0;
8441
0
            static bool output_only_modified = true;
8442
0
            if (Button("Export"))
8443
0
            {
8444
0
                if (output_dest == 0)
8445
0
                    LogToClipboard();
8446
0
                else
8447
0
                    LogToTTY();
8448
0
                LogText("ImVec4* colors = GetStyle().Colors;" IM_NEWLINE);
8449
0
                for (int i = 0; i < ImGuiCol_COUNT; i++)
8450
0
                {
8451
0
                    const ImVec4& col = style.Colors[i];
8452
0
                    const char* name = GetStyleColorName(i);
8453
0
                    if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)
8454
0
                        LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE,
8455
0
                            name, 23 - (int)strlen(name), "", col.x, col.y, col.z, col.w);
8456
0
                }
8457
0
                LogFinish();
8458
0
            }
8459
0
            SameLine(); SetNextItemWidth(120); Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0");
8460
0
            SameLine(); Checkbox("Only Modified Colors", &output_only_modified);
8461
8462
0
            static ImGuiTextFilter filter;
8463
0
            filter.Draw("Filter colors", GetFontSize() * 16);
8464
8465
0
            static ImGuiColorEditFlags alpha_flags = 0;
8466
0
            if (RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque))       { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } SameLine();
8467
0
            if (RadioButton("Alpha",  alpha_flags == ImGuiColorEditFlags_None))              { alpha_flags = ImGuiColorEditFlags_None; } SameLine();
8468
0
            if (RadioButton("Both",   alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf))  { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } SameLine();
8469
0
            HelpMarker(
8470
0
                "In the color list:\n"
8471
0
                "Left-click on color square to open color picker,\n"
8472
0
                "Right-click to open edit options menu.");
8473
8474
0
            SetNextWindowSizeConstraints(ImVec2(0.0f, GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));
8475
0
            BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);
8476
0
            PushItemWidth(GetFontSize() * -12);
8477
0
            for (int i = 0; i < ImGuiCol_COUNT; i++)
8478
0
            {
8479
0
                const char* name = GetStyleColorName(i);
8480
0
                if (!filter.PassFilter(name))
8481
0
                    continue;
8482
0
                PushID(i);
8483
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8484
0
                if (Button("?"))
8485
0
                    DebugFlashStyleColor((ImGuiCol)i);
8486
0
                SetItemTooltip("Flash given color to identify places where it is used.");
8487
0
                SameLine();
8488
0
#endif
8489
0
                ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);
8490
0
                if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)
8491
0
                {
8492
                    // Tips: in a real user application, you may want to merge and use an icon font into the main font,
8493
                    // so instead of "Save"/"Revert" you'd use icons!
8494
                    // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!
8495
0
                    SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Save")) { ref->Colors[i] = style.Colors[i]; }
8496
0
                    SameLine(0.0f, style.ItemInnerSpacing.x); if (Button("Revert")) { style.Colors[i] = ref->Colors[i]; }
8497
0
                }
8498
0
                SameLine(0.0f, style.ItemInnerSpacing.x);
8499
0
                TextUnformatted(name);
8500
0
                PopID();
8501
0
            }
8502
0
            PopItemWidth();
8503
0
            EndChild();
8504
8505
0
            EndTabItem();
8506
0
        }
8507
8508
0
        if (BeginTabItem("Fonts"))
8509
0
        {
8510
0
            ImGuiIO& io = GetIO();
8511
0
            ImFontAtlas* atlas = io.Fonts;
8512
0
            ShowFontAtlas(atlas);
8513
8514
            // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.
8515
            // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).
8516
            /*
8517
            SeparatorText("Legacy Scaling");
8518
            const float MIN_SCALE = 0.3f;
8519
            const float MAX_SCALE = 2.0f;
8520
            HelpMarker(
8521
                "Those are old settings provided for convenience.\n"
8522
                "However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, "
8523
                "rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\n"
8524
                "Using those settings here will give you poor quality results.");
8525
            PushItemWidth(GetFontSize() * 8);
8526
            DragFloat("global scale", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp); // Scale everything
8527
            //static float window_scale = 1.0f;
8528
            //if (DragFloat("window scale", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, "%.2f", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window
8529
            //    SetWindowFontScale(window_scale);
8530
            PopItemWidth();
8531
            */
8532
8533
0
            EndTabItem();
8534
0
        }
8535
8536
0
        if (BeginTabItem("Rendering"))
8537
0
        {
8538
0
            Checkbox("Anti-aliased lines", &style.AntiAliasedLines);
8539
0
            SameLine();
8540
0
            HelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.");
8541
8542
0
            Checkbox("Anti-aliased lines use texture", &style.AntiAliasedLinesUseTex);
8543
0
            SameLine();
8544
0
            HelpMarker("Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).");
8545
8546
0
            Checkbox("Anti-aliased fill", &style.AntiAliasedFill);
8547
0
            PushItemWidth(GetFontSize() * 8);
8548
0
            DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f");
8549
0
            if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;
8550
8551
            // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles.
8552
0
            DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp);
8553
0
            const bool show_samples = IsItemActive();
8554
0
            if (show_samples)
8555
0
                SetNextWindowPos(GetCursorScreenPos());
8556
0
            if (show_samples && BeginTooltip())
8557
0
            {
8558
0
                TextUnformatted("(R = radius, N = approx number of segments)");
8559
0
                Spacing();
8560
0
                ImDrawList* draw_list = GetWindowDrawList();
8561
0
                const float min_widget_width = CalcTextSize("R: MMM\nN: MMM").x;
8562
0
                for (int n = 0; n < 8; n++)
8563
0
                {
8564
0
                    const float RAD_MIN = 5.0f;
8565
0
                    const float RAD_MAX = 70.0f;
8566
0
                    const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);
8567
8568
0
                    BeginGroup();
8569
8570
                    // N is not always exact here due to how PathArcTo() function work internally
8571
0
                    Text("R: %.f\nN: %d", rad, draw_list->_CalcCircleAutoSegmentCount(rad));
8572
8573
0
                    const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);
8574
0
                    const float offset_x     = floorf(canvas_width * 0.5f);
8575
0
                    const float offset_y     = floorf(RAD_MAX);
8576
8577
0
                    const ImVec2 p1 = GetCursorScreenPos();
8578
0
                    draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8579
0
                    Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8580
8581
                    /*
8582
                    const ImVec2 p2 = GetCursorScreenPos();
8583
                    draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, GetColorU32(ImGuiCol_Text));
8584
                    Dummy(ImVec2(canvas_width, RAD_MAX * 2));
8585
                    */
8586
8587
0
                    EndGroup();
8588
0
                    SameLine();
8589
0
                }
8590
0
                EndTooltip();
8591
0
            }
8592
0
            SameLine();
8593
0
            HelpMarker("When drawing circle primitives with \"num_segments == 0\" tessellation will be calculated automatically.");
8594
8595
0
            DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.
8596
0
            DragFloat("Disabled Alpha", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, "%.2f"); SameLine(); HelpMarker("Additional alpha multiplier for disabled items (multiply over current value of Alpha).");
8597
0
            PopItemWidth();
8598
8599
0
            EndTabItem();
8600
0
        }
8601
8602
0
        EndTabBar();
8603
0
    }
8604
0
    PopItemWidth();
8605
0
}
8606
8607
//-----------------------------------------------------------------------------
8608
// [SECTION] User Guide / ShowUserGuide()
8609
//-----------------------------------------------------------------------------
8610
8611
// We omit the ImGui:: prefix in this function, as we don't expect user to be copy and pasting this code.
8612
void ImGui::ShowUserGuide()
8613
0
{
8614
0
    ImGuiIO& io = GetIO();
8615
0
    BulletText("Double-click on title bar to collapse window.");
8616
0
    BulletText(
8617
0
        "Click and drag on lower corner to resize window\n"
8618
0
        "(double-click to auto fit window to its contents).");
8619
0
    BulletText("CTRL+Click on a slider or drag box to input value as text.");
8620
0
    BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
8621
0
    BulletText("CTRL+Tab to select a window.");
8622
0
    if (io.FontAllowUserScaling)
8623
0
        BulletText("CTRL+Mouse Wheel to zoom window contents.");
8624
0
    BulletText("While inputting text:\n");
8625
0
    Indent();
8626
0
    BulletText("CTRL+Left/Right to word jump.");
8627
0
    BulletText("CTRL+A or double-click to select all.");
8628
0
    BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
8629
0
    BulletText("CTRL+Z to undo, CTRL+Y/CTRL+SHIFT+Z to redo.");
8630
0
    BulletText("ESCAPE to revert.");
8631
0
    Unindent();
8632
0
    BulletText("With keyboard navigation enabled:");
8633
0
    Indent();
8634
0
    BulletText("Arrow keys to navigate.");
8635
0
    BulletText("Space to activate a widget.");
8636
0
    BulletText("Return to input text into a widget.");
8637
0
    BulletText("Escape to deactivate a widget, close popup, exit child window.");
8638
0
    BulletText("Alt to jump to the menu layer of a window.");
8639
0
    Unindent();
8640
0
}
8641
8642
//-----------------------------------------------------------------------------
8643
// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()
8644
//-----------------------------------------------------------------------------
8645
// - ShowExampleAppMainMenuBar()
8646
// - ShowExampleMenuFile()
8647
//-----------------------------------------------------------------------------
8648
8649
// Demonstrate creating a "main" fullscreen menu bar and populating it.
8650
// Note the difference between BeginMainMenuBar() and BeginMenuBar():
8651
// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)
8652
// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.
8653
static void ShowExampleAppMainMenuBar()
8654
0
{
8655
0
    if (ImGui::BeginMainMenuBar())
8656
0
    {
8657
0
        if (ImGui::BeginMenu("File"))
8658
0
        {
8659
0
            ShowExampleMenuFile();
8660
0
            ImGui::EndMenu();
8661
0
        }
8662
0
        if (ImGui::BeginMenu("Edit"))
8663
0
        {
8664
0
            if (ImGui::MenuItem("Undo", "CTRL+Z")) {}
8665
0
            if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item
8666
0
            ImGui::Separator();
8667
0
            if (ImGui::MenuItem("Cut", "CTRL+X")) {}
8668
0
            if (ImGui::MenuItem("Copy", "CTRL+C")) {}
8669
0
            if (ImGui::MenuItem("Paste", "CTRL+V")) {}
8670
0
            ImGui::EndMenu();
8671
0
        }
8672
0
        ImGui::EndMainMenuBar();
8673
0
    }
8674
0
}
8675
8676
// Note that shortcuts are currently provided for display only
8677
// (future version will add explicit flags to BeginMenu() to request processing shortcuts)
8678
static void ShowExampleMenuFile()
8679
0
{
8680
0
    IMGUI_DEMO_MARKER("Examples/Menu");
8681
0
    ImGui::MenuItem("(demo menu)", NULL, false, false);
8682
0
    if (ImGui::MenuItem("New")) {}
8683
0
    if (ImGui::MenuItem("Open", "Ctrl+O")) {}
8684
0
    if (ImGui::BeginMenu("Open Recent"))
8685
0
    {
8686
0
        ImGui::MenuItem("fish_hat.c");
8687
0
        ImGui::MenuItem("fish_hat.inl");
8688
0
        ImGui::MenuItem("fish_hat.h");
8689
0
        if (ImGui::BeginMenu("More.."))
8690
0
        {
8691
0
            ImGui::MenuItem("Hello");
8692
0
            ImGui::MenuItem("Sailor");
8693
0
            if (ImGui::BeginMenu("Recurse.."))
8694
0
            {
8695
0
                ShowExampleMenuFile();
8696
0
                ImGui::EndMenu();
8697
0
            }
8698
0
            ImGui::EndMenu();
8699
0
        }
8700
0
        ImGui::EndMenu();
8701
0
    }
8702
0
    if (ImGui::MenuItem("Save", "Ctrl+S")) {}
8703
0
    if (ImGui::MenuItem("Save As..")) {}
8704
8705
0
    ImGui::Separator();
8706
0
    IMGUI_DEMO_MARKER("Examples/Menu/Options");
8707
0
    if (ImGui::BeginMenu("Options"))
8708
0
    {
8709
0
        static bool enabled = true;
8710
0
        ImGui::MenuItem("Enabled", "", &enabled);
8711
0
        ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Borders);
8712
0
        for (int i = 0; i < 10; i++)
8713
0
            ImGui::Text("Scrolling Text %d", i);
8714
0
        ImGui::EndChild();
8715
0
        static float f = 0.5f;
8716
0
        static int n = 0;
8717
0
        ImGui::SliderFloat("Value", &f, 0.0f, 1.0f);
8718
0
        ImGui::InputFloat("Input", &f, 0.1f);
8719
0
        ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0");
8720
0
        ImGui::EndMenu();
8721
0
    }
8722
8723
0
    IMGUI_DEMO_MARKER("Examples/Menu/Colors");
8724
0
    if (ImGui::BeginMenu("Colors"))
8725
0
    {
8726
0
        float sz = ImGui::GetTextLineHeight();
8727
0
        for (int i = 0; i < ImGuiCol_COUNT; i++)
8728
0
        {
8729
0
            const char* name = ImGui::GetStyleColorName((ImGuiCol)i);
8730
0
            ImVec2 p = ImGui::GetCursorScreenPos();
8731
0
            ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));
8732
0
            ImGui::Dummy(ImVec2(sz, sz));
8733
0
            ImGui::SameLine();
8734
0
            ImGui::MenuItem(name);
8735
0
        }
8736
0
        ImGui::EndMenu();
8737
0
    }
8738
8739
    // Here we demonstrate appending again to the "Options" menu (which we already created above)
8740
    // Of course in this demo it is a little bit silly that this function calls BeginMenu("Options") twice.
8741
    // In a real code-base using it would make senses to use this feature from very different code locations.
8742
0
    if (ImGui::BeginMenu("Options")) // <-- Append!
8743
0
    {
8744
0
        IMGUI_DEMO_MARKER("Examples/Menu/Append to an existing menu");
8745
0
        static bool b = true;
8746
0
        ImGui::Checkbox("SomeOption", &b);
8747
0
        ImGui::EndMenu();
8748
0
    }
8749
8750
0
    if (ImGui::BeginMenu("Disabled", false)) // Disabled
8751
0
    {
8752
0
        IM_ASSERT(0);
8753
0
    }
8754
0
    if (ImGui::MenuItem("Checked", NULL, true)) {}
8755
0
    ImGui::Separator();
8756
0
    if (ImGui::MenuItem("Quit", "Alt+F4")) {}
8757
0
}
8758
8759
//-----------------------------------------------------------------------------
8760
// [SECTION] Example App: Debug Console / ShowExampleAppConsole()
8761
//-----------------------------------------------------------------------------
8762
8763
// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.
8764
// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.
8765
struct ExampleAppConsole
8766
{
8767
    char                  InputBuf[256];
8768
    ImVector<char*>       Items;
8769
    ImVector<const char*> Commands;
8770
    ImVector<char*>       History;
8771
    int                   HistoryPos;    // -1: new line, 0..History.Size-1 browsing history.
8772
    ImGuiTextFilter       Filter;
8773
    bool                  AutoScroll;
8774
    bool                  ScrollToBottom;
8775
8776
    ExampleAppConsole()
8777
0
    {
8778
0
        IMGUI_DEMO_MARKER("Examples/Console");
8779
0
        ClearLog();
8780
0
        memset(InputBuf, 0, sizeof(InputBuf));
8781
0
        HistoryPos = -1;
8782
8783
        // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
8784
0
        Commands.push_back("HELP");
8785
0
        Commands.push_back("HISTORY");
8786
0
        Commands.push_back("CLEAR");
8787
0
        Commands.push_back("CLASSIFY");
8788
0
        AutoScroll = true;
8789
0
        ScrollToBottom = false;
8790
0
        AddLog("Welcome to Dear ImGui!");
8791
0
    }
8792
    ~ExampleAppConsole()
8793
0
    {
8794
0
        ClearLog();
8795
0
        for (int i = 0; i < History.Size; i++)
8796
0
            ImGui::MemFree(History[i]);
8797
0
    }
8798
8799
    // Portable helpers
8800
0
    static int   Stricmp(const char* s1, const char* s2)         { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }
8801
0
    static int   Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }
8802
0
    static char* Strdup(const char* s)                           { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }
8803
0
    static void  Strtrim(char* s)                                { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }
8804
8805
    void    ClearLog()
8806
0
    {
8807
0
        for (int i = 0; i < Items.Size; i++)
8808
0
            ImGui::MemFree(Items[i]);
8809
0
        Items.clear();
8810
0
    }
8811
8812
    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)
8813
0
    {
8814
        // FIXME-OPT
8815
0
        char buf[1024];
8816
0
        va_list args;
8817
0
        va_start(args, fmt);
8818
0
        vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);
8819
0
        buf[IM_ARRAYSIZE(buf)-1] = 0;
8820
0
        va_end(args);
8821
0
        Items.push_back(Strdup(buf));
8822
0
    }
8823
8824
    void    Draw(const char* title, bool* p_open)
8825
0
    {
8826
0
        ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
8827
0
        if (!ImGui::Begin(title, p_open))
8828
0
        {
8829
0
            ImGui::End();
8830
0
            return;
8831
0
        }
8832
8833
        // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.
8834
        // So e.g. IsItemHovered() will return true when hovering the title bar.
8835
        // Here we create a context menu only available from the title bar.
8836
0
        if (ImGui::BeginPopupContextItem())
8837
0
        {
8838
0
            if (ImGui::MenuItem("Close Console"))
8839
0
                *p_open = false;
8840
0
            ImGui::EndPopup();
8841
0
        }
8842
8843
0
        ImGui::TextWrapped(
8844
0
            "This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate "
8845
0
            "implementation may want to store entries along with extra data such as timestamp, emitter, etc.");
8846
0
        ImGui::TextWrapped("Enter 'HELP' for help.");
8847
8848
        // TODO: display items starting from the bottom
8849
8850
0
        if (ImGui::SmallButton("Add Debug Text"))  { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); }
8851
0
        ImGui::SameLine();
8852
0
        if (ImGui::SmallButton("Add Debug Error")) { AddLog("[error] something went wrong"); }
8853
0
        ImGui::SameLine();
8854
0
        if (ImGui::SmallButton("Clear"))           { ClearLog(); }
8855
0
        ImGui::SameLine();
8856
0
        bool copy_to_clipboard = ImGui::SmallButton("Copy");
8857
        //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
8858
8859
0
        ImGui::Separator();
8860
8861
        // Options menu
8862
0
        if (ImGui::BeginPopup("Options"))
8863
0
        {
8864
0
            ImGui::Checkbox("Auto-scroll", &AutoScroll);
8865
0
            ImGui::EndPopup();
8866
0
        }
8867
8868
        // Options, Filter
8869
0
        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_O, ImGuiInputFlags_Tooltip);
8870
0
        if (ImGui::Button("Options"))
8871
0
            ImGui::OpenPopup("Options");
8872
0
        ImGui::SameLine();
8873
0
        Filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180);
8874
0
        ImGui::Separator();
8875
8876
        // Reserve enough left-over height for 1 separator + 1 input text
8877
0
        const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
8878
0
        if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_NavFlattened, ImGuiWindowFlags_HorizontalScrollbar))
8879
0
        {
8880
0
            if (ImGui::BeginPopupContextWindow())
8881
0
            {
8882
0
                if (ImGui::Selectable("Clear")) ClearLog();
8883
0
                ImGui::EndPopup();
8884
0
            }
8885
8886
            // Display every line as a separate entry so we can change their color or add custom widgets.
8887
            // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());
8888
            // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping
8889
            // to only process visible items. The clipper will automatically measure the height of your first item and then
8890
            // "seek" to display only items in the visible area.
8891
            // To use the clipper we can replace your standard loop:
8892
            //      for (int i = 0; i < Items.Size; i++)
8893
            //   With:
8894
            //      ImGuiListClipper clipper;
8895
            //      clipper.Begin(Items.Size);
8896
            //      while (clipper.Step())
8897
            //         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8898
            // - That your items are evenly spaced (same height)
8899
            // - That you have cheap random access to your elements (you can access them given their index,
8900
            //   without processing all the ones before)
8901
            // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.
8902
            // We would need random-access on the post-filtered list.
8903
            // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices
8904
            // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,
8905
            // and appending newly elements as they are inserted. This is left as a task to the user until we can manage
8906
            // to improve this example code!
8907
            // If your items are of variable height:
8908
            // - Split them into same height items would be simpler and facilitate random-seeking into your list.
8909
            // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.
8910
0
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing
8911
0
            if (copy_to_clipboard)
8912
0
                ImGui::LogToClipboard();
8913
0
            for (const char* item : Items)
8914
0
            {
8915
0
                if (!Filter.PassFilter(item))
8916
0
                    continue;
8917
8918
                // Normally you would store more information in your item than just a string.
8919
                // (e.g. make Items[] an array of structure, store color/type etc.)
8920
0
                ImVec4 color;
8921
0
                bool has_color = false;
8922
0
                if (strstr(item, "[error]")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
8923
0
                else if (strncmp(item, "# ", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
8924
0
                if (has_color)
8925
0
                    ImGui::PushStyleColor(ImGuiCol_Text, color);
8926
0
                ImGui::TextUnformatted(item);
8927
0
                if (has_color)
8928
0
                    ImGui::PopStyleColor();
8929
0
            }
8930
0
            if (copy_to_clipboard)
8931
0
                ImGui::LogFinish();
8932
8933
            // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
8934
            // Using a scrollbar or mouse-wheel will take away from the bottom edge.
8935
0
            if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
8936
0
                ImGui::SetScrollHereY(1.0f);
8937
0
            ScrollToBottom = false;
8938
8939
0
            ImGui::PopStyleVar();
8940
0
        }
8941
0
        ImGui::EndChild();
8942
0
        ImGui::Separator();
8943
8944
        // Command-line
8945
0
        bool reclaim_focus = false;
8946
0
        ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;
8947
0
        if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))
8948
0
        {
8949
0
            char* s = InputBuf;
8950
0
            Strtrim(s);
8951
0
            if (s[0])
8952
0
                ExecCommand(s);
8953
0
            strcpy(s, "");
8954
0
            reclaim_focus = true;
8955
0
        }
8956
8957
        // Auto-focus on window apparition
8958
0
        ImGui::SetItemDefaultFocus();
8959
0
        if (reclaim_focus)
8960
0
            ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
8961
8962
0
        ImGui::End();
8963
0
    }
8964
8965
    void    ExecCommand(const char* command_line)
8966
0
    {
8967
0
        AddLog("# %s\n", command_line);
8968
8969
        // Insert into history. First find match and delete it so it can be pushed to the back.
8970
        // This isn't trying to be smart or optimal.
8971
0
        HistoryPos = -1;
8972
0
        for (int i = History.Size - 1; i >= 0; i--)
8973
0
            if (Stricmp(History[i], command_line) == 0)
8974
0
            {
8975
0
                ImGui::MemFree(History[i]);
8976
0
                History.erase(History.begin() + i);
8977
0
                break;
8978
0
            }
8979
0
        History.push_back(Strdup(command_line));
8980
8981
        // Process command
8982
0
        if (Stricmp(command_line, "CLEAR") == 0)
8983
0
        {
8984
0
            ClearLog();
8985
0
        }
8986
0
        else if (Stricmp(command_line, "HELP") == 0)
8987
0
        {
8988
0
            AddLog("Commands:");
8989
0
            for (int i = 0; i < Commands.Size; i++)
8990
0
                AddLog("- %s", Commands[i]);
8991
0
        }
8992
0
        else if (Stricmp(command_line, "HISTORY") == 0)
8993
0
        {
8994
0
            int first = History.Size - 10;
8995
0
            for (int i = first > 0 ? first : 0; i < History.Size; i++)
8996
0
                AddLog("%3d: %s\n", i, History[i]);
8997
0
        }
8998
0
        else
8999
0
        {
9000
0
            AddLog("Unknown command: '%s'\n", command_line);
9001
0
        }
9002
9003
        // On command input, we scroll to bottom even if AutoScroll==false
9004
0
        ScrollToBottom = true;
9005
0
    }
9006
9007
    // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks
9008
    static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)
9009
0
    {
9010
0
        ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;
9011
0
        return console->TextEditCallback(data);
9012
0
    }
9013
9014
    int     TextEditCallback(ImGuiInputTextCallbackData* data)
9015
0
    {
9016
        //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd);
9017
0
        switch (data->EventFlag)
9018
0
        {
9019
0
        case ImGuiInputTextFlags_CallbackCompletion:
9020
0
            {
9021
                // Example of TEXT COMPLETION
9022
9023
                // Locate beginning of current word
9024
0
                const char* word_end = data->Buf + data->CursorPos;
9025
0
                const char* word_start = word_end;
9026
0
                while (word_start > data->Buf)
9027
0
                {
9028
0
                    const char c = word_start[-1];
9029
0
                    if (c == ' ' || c == '\t' || c == ',' || c == ';')
9030
0
                        break;
9031
0
                    word_start--;
9032
0
                }
9033
9034
                // Build a list of candidates
9035
0
                ImVector<const char*> candidates;
9036
0
                for (int i = 0; i < Commands.Size; i++)
9037
0
                    if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
9038
0
                        candidates.push_back(Commands[i]);
9039
9040
0
                if (candidates.Size == 0)
9041
0
                {
9042
                    // No match
9043
0
                    AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
9044
0
                }
9045
0
                else if (candidates.Size == 1)
9046
0
                {
9047
                    // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
9048
0
                    data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
9049
0
                    data->InsertChars(data->CursorPos, candidates[0]);
9050
0
                    data->InsertChars(data->CursorPos, " ");
9051
0
                }
9052
0
                else
9053
0
                {
9054
                    // Multiple matches. Complete as much as we can..
9055
                    // So inputting "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
9056
0
                    int match_len = (int)(word_end - word_start);
9057
0
                    for (;;)
9058
0
                    {
9059
0
                        int c = 0;
9060
0
                        bool all_candidates_matches = true;
9061
0
                        for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
9062
0
                            if (i == 0)
9063
0
                                c = toupper(candidates[i][match_len]);
9064
0
                            else if (c == 0 || c != toupper(candidates[i][match_len]))
9065
0
                                all_candidates_matches = false;
9066
0
                        if (!all_candidates_matches)
9067
0
                            break;
9068
0
                        match_len++;
9069
0
                    }
9070
9071
0
                    if (match_len > 0)
9072
0
                    {
9073
0
                        data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
9074
0
                        data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
9075
0
                    }
9076
9077
                    // List matches
9078
0
                    AddLog("Possible matches:\n");
9079
0
                    for (int i = 0; i < candidates.Size; i++)
9080
0
                        AddLog("- %s\n", candidates[i]);
9081
0
                }
9082
9083
0
                break;
9084
0
            }
9085
0
        case ImGuiInputTextFlags_CallbackHistory:
9086
0
            {
9087
                // Example of HISTORY
9088
0
                const int prev_history_pos = HistoryPos;
9089
0
                if (data->EventKey == ImGuiKey_UpArrow)
9090
0
                {
9091
0
                    if (HistoryPos == -1)
9092
0
                        HistoryPos = History.Size - 1;
9093
0
                    else if (HistoryPos > 0)
9094
0
                        HistoryPos--;
9095
0
                }
9096
0
                else if (data->EventKey == ImGuiKey_DownArrow)
9097
0
                {
9098
0
                    if (HistoryPos != -1)
9099
0
                        if (++HistoryPos >= History.Size)
9100
0
                            HistoryPos = -1;
9101
0
                }
9102
9103
                // A better implementation would preserve the data on the current input line along with cursor position.
9104
0
                if (prev_history_pos != HistoryPos)
9105
0
                {
9106
0
                    const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : "";
9107
0
                    data->DeleteChars(0, data->BufTextLen);
9108
0
                    data->InsertChars(0, history_str);
9109
0
                }
9110
0
            }
9111
0
        }
9112
0
        return 0;
9113
0
    }
9114
};
9115
9116
static void ShowExampleAppConsole(bool* p_open)
9117
0
{
9118
0
    static ExampleAppConsole console;
9119
0
    console.Draw("Example: Console", p_open);
9120
0
}
9121
9122
//-----------------------------------------------------------------------------
9123
// [SECTION] Example App: Debug Log / ShowExampleAppLog()
9124
//-----------------------------------------------------------------------------
9125
9126
// Usage:
9127
//  static ExampleAppLog my_log;
9128
//  my_log.AddLog("Hello %d world\n", 123);
9129
//  my_log.Draw("title");
9130
struct ExampleAppLog
9131
{
9132
    ImGuiTextBuffer     Buf;
9133
    ImGuiTextFilter     Filter;
9134
    ImVector<int>       LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
9135
    bool                AutoScroll;  // Keep scrolling if already at the bottom.
9136
9137
    ExampleAppLog()
9138
0
    {
9139
0
        AutoScroll = true;
9140
0
        Clear();
9141
0
    }
9142
9143
    void    Clear()
9144
0
    {
9145
0
        Buf.clear();
9146
0
        LineOffsets.clear();
9147
0
        LineOffsets.push_back(0);
9148
0
    }
9149
9150
    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)
9151
0
    {
9152
0
        int old_size = Buf.size();
9153
0
        va_list args;
9154
0
        va_start(args, fmt);
9155
0
        Buf.appendfv(fmt, args);
9156
0
        va_end(args);
9157
0
        for (int new_size = Buf.size(); old_size < new_size; old_size++)
9158
0
            if (Buf[old_size] == '\n')
9159
0
                LineOffsets.push_back(old_size + 1);
9160
0
    }
9161
9162
    void    Draw(const char* title, bool* p_open = NULL)
9163
0
    {
9164
0
        if (!ImGui::Begin(title, p_open))
9165
0
        {
9166
0
            ImGui::End();
9167
0
            return;
9168
0
        }
9169
9170
        // Options menu
9171
0
        if (ImGui::BeginPopup("Options"))
9172
0
        {
9173
0
            ImGui::Checkbox("Auto-scroll", &AutoScroll);
9174
0
            ImGui::EndPopup();
9175
0
        }
9176
9177
        // Main window
9178
0
        if (ImGui::Button("Options"))
9179
0
            ImGui::OpenPopup("Options");
9180
0
        ImGui::SameLine();
9181
0
        bool clear = ImGui::Button("Clear");
9182
0
        ImGui::SameLine();
9183
0
        bool copy = ImGui::Button("Copy");
9184
0
        ImGui::SameLine();
9185
0
        Filter.Draw("Filter", -100.0f);
9186
9187
0
        ImGui::Separator();
9188
9189
0
        if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))
9190
0
        {
9191
0
            if (clear)
9192
0
                Clear();
9193
0
            if (copy)
9194
0
                ImGui::LogToClipboard();
9195
9196
0
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9197
0
            const char* buf = Buf.begin();
9198
0
            const char* buf_end = Buf.end();
9199
0
            if (Filter.IsActive())
9200
0
            {
9201
                // In this example we don't use the clipper when Filter is enabled.
9202
                // This is because we don't have random access to the result of our filter.
9203
                // A real application processing logs with ten of thousands of entries may want to store the result of
9204
                // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).
9205
0
                for (int line_no = 0; line_no < LineOffsets.Size; line_no++)
9206
0
                {
9207
0
                    const char* line_start = buf + LineOffsets[line_no];
9208
0
                    const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9209
0
                    if (Filter.PassFilter(line_start, line_end))
9210
0
                        ImGui::TextUnformatted(line_start, line_end);
9211
0
                }
9212
0
            }
9213
0
            else
9214
0
            {
9215
                // The simplest and easy way to display the entire buffer:
9216
                //   ImGui::TextUnformatted(buf_begin, buf_end);
9217
                // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward
9218
                // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are
9219
                // within the visible area.
9220
                // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them
9221
                // on your side is recommended. Using ImGuiListClipper requires
9222
                // - A) random access into your data
9223
                // - B) items all being the  same height,
9224
                // both of which we can handle since we have an array pointing to the beginning of each line of text.
9225
                // When using the filter (in the block of code above) we don't have random access into the data to display
9226
                // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make
9227
                // it possible (and would be recommended if you want to search through tens of thousands of entries).
9228
0
                ImGuiListClipper clipper;
9229
0
                clipper.Begin(LineOffsets.Size);
9230
0
                while (clipper.Step())
9231
0
                {
9232
0
                    for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
9233
0
                    {
9234
0
                        const char* line_start = buf + LineOffsets[line_no];
9235
0
                        const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
9236
0
                        ImGui::TextUnformatted(line_start, line_end);
9237
0
                    }
9238
0
                }
9239
0
                clipper.End();
9240
0
            }
9241
0
            ImGui::PopStyleVar();
9242
9243
            // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
9244
            // Using a scrollbar or mouse-wheel will take away from the bottom edge.
9245
0
            if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
9246
0
                ImGui::SetScrollHereY(1.0f);
9247
0
        }
9248
0
        ImGui::EndChild();
9249
0
        ImGui::End();
9250
0
    }
9251
};
9252
9253
// Demonstrate creating a simple log window with basic filtering.
9254
static void ShowExampleAppLog(bool* p_open)
9255
0
{
9256
0
    static ExampleAppLog log;
9257
9258
    // For the demo: add a debug button _BEFORE_ the normal log window contents
9259
    // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.
9260
    // Most of the contents of the window will be added by the log.Draw() call.
9261
0
    ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);
9262
0
    ImGui::Begin("Example: Log", p_open);
9263
0
    IMGUI_DEMO_MARKER("Examples/Log");
9264
0
    if (ImGui::SmallButton("[Debug] Add 5 entries"))
9265
0
    {
9266
0
        static int counter = 0;
9267
0
        const char* categories[3] = { "info", "warn", "error" };
9268
0
        const char* words[] = { "Bumfuzzled", "Cattywampus", "Snickersnee", "Abibliophobia", "Absquatulate", "Nincompoop", "Pauciloquent" };
9269
0
        for (int n = 0; n < 5; n++)
9270
0
        {
9271
0
            const char* category = categories[counter % IM_ARRAYSIZE(categories)];
9272
0
            const char* word = words[counter % IM_ARRAYSIZE(words)];
9273
0
            log.AddLog("[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\n",
9274
0
                ImGui::GetFrameCount(), category, ImGui::GetTime(), word);
9275
0
            counter++;
9276
0
        }
9277
0
    }
9278
0
    ImGui::End();
9279
9280
    // Actually call in the regular Log helper (which will Begin() into the same window as we just did)
9281
0
    log.Draw("Example: Log", p_open);
9282
0
}
9283
9284
//-----------------------------------------------------------------------------
9285
// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()
9286
//-----------------------------------------------------------------------------
9287
9288
// Demonstrate create a window with multiple child windows.
9289
static void ShowExampleAppLayout(bool* p_open)
9290
0
{
9291
0
    ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver);
9292
0
    if (ImGui::Begin("Example: Simple layout", p_open, ImGuiWindowFlags_MenuBar))
9293
0
    {
9294
0
        IMGUI_DEMO_MARKER("Examples/Simple layout");
9295
0
        if (ImGui::BeginMenuBar())
9296
0
        {
9297
0
            if (ImGui::BeginMenu("File"))
9298
0
            {
9299
0
                if (ImGui::MenuItem("Close", "Ctrl+W")) { *p_open = false; }
9300
0
                ImGui::EndMenu();
9301
0
            }
9302
0
            ImGui::EndMenuBar();
9303
0
        }
9304
9305
        // Left
9306
0
        static int selected = 0;
9307
0
        {
9308
0
            ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeX);
9309
0
            for (int i = 0; i < 100; i++)
9310
0
            {
9311
0
                char label[128];
9312
0
                sprintf(label, "MyObject %d", i);
9313
0
                if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SelectOnNav))
9314
0
                    selected = i;
9315
0
            }
9316
0
            ImGui::EndChild();
9317
0
        }
9318
0
        ImGui::SameLine();
9319
9320
        // Right
9321
0
        {
9322
0
            ImGui::BeginGroup();
9323
0
            ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
9324
0
            ImGui::Text("MyObject: %d", selected);
9325
0
            ImGui::Separator();
9326
0
            if (ImGui::BeginTabBar("##Tabs", ImGuiTabBarFlags_None))
9327
0
            {
9328
0
                if (ImGui::BeginTabItem("Description"))
9329
0
                {
9330
0
                    ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ");
9331
0
                    ImGui::EndTabItem();
9332
0
                }
9333
0
                if (ImGui::BeginTabItem("Details"))
9334
0
                {
9335
0
                    ImGui::Text("ID: 0123456789");
9336
0
                    ImGui::EndTabItem();
9337
0
                }
9338
0
                ImGui::EndTabBar();
9339
0
            }
9340
0
            ImGui::EndChild();
9341
0
            if (ImGui::Button("Revert")) {}
9342
0
            ImGui::SameLine();
9343
0
            if (ImGui::Button("Save")) {}
9344
0
            ImGui::EndGroup();
9345
0
        }
9346
0
    }
9347
0
    ImGui::End();
9348
0
}
9349
9350
//-----------------------------------------------------------------------------
9351
// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()
9352
//-----------------------------------------------------------------------------
9353
// Some of the interactions are a bit lack-luster:
9354
// - We would want pressing validating or leaving the filter to somehow restore focus.
9355
// - We may want more advanced filtering (child nodes) and clipper support: both will need extra work.
9356
// - We would want to customize some keyboard interactions to easily keyboard navigate between the tree and the properties.
9357
//-----------------------------------------------------------------------------
9358
9359
struct ExampleAppPropertyEditor
9360
{
9361
    ImGuiTextFilter     Filter;
9362
    ExampleTreeNode*    VisibleNode = NULL;
9363
9364
    void Draw(ExampleTreeNode* root_node)
9365
0
    {
9366
        // Left side: draw tree
9367
        // - Currently using a table to benefit from RowBg feature
9368
0
        if (ImGui::BeginChild("##tree", ImVec2(300, 0), ImGuiChildFlags_ResizeX | ImGuiChildFlags_Borders | ImGuiChildFlags_NavFlattened))
9369
0
        {
9370
0
            ImGui::SetNextItemWidth(-FLT_MIN);
9371
0
            ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_F, ImGuiInputFlags_Tooltip);
9372
0
            ImGui::PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
9373
0
            if (ImGui::InputTextWithHint("##Filter", "incl,-excl", Filter.InputBuf, IM_ARRAYSIZE(Filter.InputBuf), ImGuiInputTextFlags_EscapeClearsAll))
9374
0
                Filter.Build();
9375
0
            ImGui::PopItemFlag();
9376
9377
0
            if (ImGui::BeginTable("##bg", 1, ImGuiTableFlags_RowBg))
9378
0
            {
9379
0
                for (ExampleTreeNode* node : root_node->Childs)
9380
0
                    if (Filter.PassFilter(node->Name)) // Filter root node
9381
0
                        DrawTreeNode(node);
9382
0
                ImGui::EndTable();
9383
0
            }
9384
0
        }
9385
0
        ImGui::EndChild();
9386
9387
        // Right side: draw properties
9388
0
        ImGui::SameLine();
9389
9390
0
        ImGui::BeginGroup(); // Lock X position
9391
0
        if (ExampleTreeNode* node = VisibleNode)
9392
0
        {
9393
0
            ImGui::Text("%s", node->Name);
9394
0
            ImGui::TextDisabled("UID: 0x%08X", node->UID);
9395
0
            ImGui::Separator();
9396
0
            if (ImGui::BeginTable("##properties", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))
9397
0
            {
9398
                // Push object ID after we entered the table, so table is shared for all objects
9399
0
                ImGui::PushID((int)node->UID);
9400
0
                ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
9401
0
                ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 2.0f); // Default twice larger
9402
0
                if (node->HasData)
9403
0
                {
9404
                    // In a typical application, the structure description would be derived from a data-driven system.
9405
                    // - We try to mimic this with our ExampleMemberInfo structure and the ExampleTreeNodeMemberInfos[] array.
9406
                    // - Limits and some details are hard-coded to simplify the demo.
9407
0
                    for (const ExampleMemberInfo& field_desc : ExampleTreeNodeMemberInfos)
9408
0
                    {
9409
0
                        ImGui::TableNextRow();
9410
0
                        ImGui::PushID(field_desc.Name);
9411
0
                        ImGui::TableNextColumn();
9412
0
                        ImGui::AlignTextToFramePadding();
9413
0
                        ImGui::TextUnformatted(field_desc.Name);
9414
0
                        ImGui::TableNextColumn();
9415
0
                        void* field_ptr = (void*)(((unsigned char*)node) + field_desc.Offset);
9416
0
                        switch (field_desc.DataType)
9417
0
                        {
9418
0
                        case ImGuiDataType_Bool:
9419
0
                        {
9420
0
                            IM_ASSERT(field_desc.DataCount == 1);
9421
0
                            ImGui::Checkbox("##Editor", (bool*)field_ptr);
9422
0
                            break;
9423
0
                        }
9424
0
                        case ImGuiDataType_S32:
9425
0
                        {
9426
0
                            int v_min = INT_MIN, v_max = INT_MAX;
9427
0
                            ImGui::SetNextItemWidth(-FLT_MIN);
9428
0
                            ImGui::DragScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, 1.0f, &v_min, &v_max);
9429
0
                            break;
9430
0
                        }
9431
0
                        case ImGuiDataType_Float:
9432
0
                        {
9433
0
                            float v_min = 0.0f, v_max = 1.0f;
9434
0
                            ImGui::SetNextItemWidth(-FLT_MIN);
9435
0
                            ImGui::SliderScalarN("##Editor", field_desc.DataType, field_ptr, field_desc.DataCount, &v_min, &v_max);
9436
0
                            break;
9437
0
                        }
9438
0
                        case ImGuiDataType_String:
9439
0
                        {
9440
0
                            ImGui::InputText("##Editor", reinterpret_cast<char*>(field_ptr), 28);
9441
0
                            break;
9442
0
                        }
9443
0
                        }
9444
0
                        ImGui::PopID();
9445
0
                    }
9446
0
                }
9447
0
                ImGui::PopID();
9448
0
                ImGui::EndTable();
9449
0
            }
9450
0
        }
9451
0
        ImGui::EndGroup();
9452
0
    }
9453
9454
    void DrawTreeNode(ExampleTreeNode* node)
9455
0
    {
9456
0
        ImGui::TableNextRow();
9457
0
        ImGui::TableNextColumn();
9458
0
        ImGui::PushID(node->UID);
9459
0
        ImGuiTreeNodeFlags tree_flags = ImGuiTreeNodeFlags_None;
9460
0
        tree_flags |= ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;// Standard opening mode as we are likely to want to add selection afterwards
9461
0
        tree_flags |= ImGuiTreeNodeFlags_NavLeftJumpsToParent;  // Left arrow support
9462
0
        tree_flags |= ImGuiTreeNodeFlags_SpanFullWidth;         // Span full width for easier mouse reach
9463
0
        tree_flags |= ImGuiTreeNodeFlags_DrawLinesToNodes;      // Always draw hierarchy outlines
9464
0
        if (node == VisibleNode)
9465
0
            tree_flags |= ImGuiTreeNodeFlags_Selected;
9466
0
        if (node->Childs.Size == 0)
9467
0
            tree_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
9468
0
        if (node->DataMyBool == false)
9469
0
            ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]);
9470
0
        bool node_open = ImGui::TreeNodeEx("", tree_flags, "%s", node->Name);
9471
0
        if (node->DataMyBool == false)
9472
0
            ImGui::PopStyleColor();
9473
0
        if (ImGui::IsItemFocused())
9474
0
            VisibleNode = node;
9475
0
        if (node_open)
9476
0
        {
9477
0
            for (ExampleTreeNode* child : node->Childs)
9478
0
                DrawTreeNode(child);
9479
0
            ImGui::TreePop();
9480
0
        }
9481
0
        ImGui::PopID();
9482
0
    }
9483
};
9484
9485
// Demonstrate creating a simple property editor.
9486
static void ShowExampleAppPropertyEditor(bool* p_open, ImGuiDemoWindowData* demo_data)
9487
0
{
9488
0
    ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);
9489
0
    if (!ImGui::Begin("Example: Property editor", p_open))
9490
0
    {
9491
0
        ImGui::End();
9492
0
        return;
9493
0
    }
9494
9495
0
    IMGUI_DEMO_MARKER("Examples/Property Editor");
9496
0
    static ExampleAppPropertyEditor property_editor;
9497
0
    if (demo_data->DemoTree == NULL)
9498
0
        demo_data->DemoTree = ExampleTree_CreateDemoTree();
9499
0
    property_editor.Draw(demo_data->DemoTree);
9500
9501
0
    ImGui::End();
9502
0
}
9503
9504
//-----------------------------------------------------------------------------
9505
// [SECTION] Example App: Long Text / ShowExampleAppLongText()
9506
//-----------------------------------------------------------------------------
9507
9508
// Demonstrate/test rendering huge amount of text, and the incidence of clipping.
9509
static void ShowExampleAppLongText(bool* p_open)
9510
0
{
9511
0
    ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);
9512
0
    if (!ImGui::Begin("Example: Long text display", p_open))
9513
0
    {
9514
0
        ImGui::End();
9515
0
        return;
9516
0
    }
9517
0
    IMGUI_DEMO_MARKER("Examples/Long text display");
9518
9519
0
    static int test_type = 0;
9520
0
    static ImGuiTextBuffer log;
9521
0
    static int lines = 0;
9522
0
    ImGui::Text("Printing unusually long amount of text.");
9523
0
    ImGui::Combo("Test type", &test_type,
9524
0
        "Single call to TextUnformatted()\0"
9525
0
        "Multiple calls to Text(), clipped\0"
9526
0
        "Multiple calls to Text(), not clipped (slow)\0");
9527
0
    ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size());
9528
0
    if (ImGui::Button("Clear")) { log.clear(); lines = 0; }
9529
0
    ImGui::SameLine();
9530
0
    if (ImGui::Button("Add 1000 lines"))
9531
0
    {
9532
0
        for (int i = 0; i < 1000; i++)
9533
0
            log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines + i);
9534
0
        lines += 1000;
9535
0
    }
9536
0
    ImGui::BeginChild("Log");
9537
0
    switch (test_type)
9538
0
    {
9539
0
    case 0:
9540
        // Single call to TextUnformatted() with a big buffer
9541
0
        ImGui::TextUnformatted(log.begin(), log.end());
9542
0
        break;
9543
0
    case 1:
9544
0
        {
9545
            // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
9546
0
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9547
0
            ImGuiListClipper clipper;
9548
0
            clipper.Begin(lines);
9549
0
            while (clipper.Step())
9550
0
                for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
9551
0
                    ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
9552
0
            ImGui::PopStyleVar();
9553
0
            break;
9554
0
        }
9555
0
    case 2:
9556
        // Multiple calls to Text(), not clipped (slow)
9557
0
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
9558
0
        for (int i = 0; i < lines; i++)
9559
0
            ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
9560
0
        ImGui::PopStyleVar();
9561
0
        break;
9562
0
    }
9563
0
    ImGui::EndChild();
9564
0
    ImGui::End();
9565
0
}
9566
9567
//-----------------------------------------------------------------------------
9568
// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()
9569
//-----------------------------------------------------------------------------
9570
9571
// Demonstrate creating a window which gets auto-resized according to its content.
9572
static void ShowExampleAppAutoResize(bool* p_open)
9573
0
{
9574
0
    if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize))
9575
0
    {
9576
0
        ImGui::End();
9577
0
        return;
9578
0
    }
9579
0
    IMGUI_DEMO_MARKER("Examples/Auto-resizing window");
9580
9581
0
    static int lines = 10;
9582
0
    ImGui::TextUnformatted(
9583
0
        "Window will resize every-frame to the size of its content.\n"
9584
0
        "Note that you probably don't want to query the window size to\n"
9585
0
        "output your content because that would create a feedback loop.");
9586
0
    ImGui::SliderInt("Number of lines", &lines, 1, 20);
9587
0
    for (int i = 0; i < lines; i++)
9588
0
        ImGui::Text("%*sThis is line %d", i * 4, "", i); // Pad with space to extend size horizontally
9589
0
    ImGui::End();
9590
0
}
9591
9592
//-----------------------------------------------------------------------------
9593
// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()
9594
//-----------------------------------------------------------------------------
9595
9596
// Demonstrate creating a window with custom resize constraints.
9597
// Note that size constraints currently don't work on a docked window (when in 'docking' branch)
9598
static void ShowExampleAppConstrainedResize(bool* p_open)
9599
0
{
9600
0
    struct CustomConstraints
9601
0
    {
9602
        // Helper functions to demonstrate programmatic constraints
9603
        // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.
9604
        // FIXME: None of the three demos works consistently when resizing from borders.
9605
0
        static void AspectRatio(ImGuiSizeCallbackData* data)
9606
0
        {
9607
0
            float aspect_ratio = *(float*)data->UserData;
9608
0
            data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);
9609
0
        }
9610
0
        static void Square(ImGuiSizeCallbackData* data)
9611
0
        {
9612
0
            data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);
9613
0
        }
9614
0
        static void Step(ImGuiSizeCallbackData* data)
9615
0
        {
9616
0
            float step = *(float*)data->UserData;
9617
0
            data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);
9618
0
        }
9619
0
    };
9620
9621
0
    const char* test_desc[] =
9622
0
    {
9623
0
        "Between 100x100 and 500x500",
9624
0
        "At least 100x100",
9625
0
        "Resize vertical + lock current width",
9626
0
        "Resize horizontal + lock current height",
9627
0
        "Width Between 400 and 500",
9628
0
        "Height at least 400",
9629
0
        "Custom: Aspect Ratio 16:9",
9630
0
        "Custom: Always Square",
9631
0
        "Custom: Fixed Steps (100)",
9632
0
    };
9633
9634
    // Options
9635
0
    static bool auto_resize = false;
9636
0
    static bool window_padding = true;
9637
0
    static int type = 6; // Aspect Ratio
9638
0
    static int display_lines = 10;
9639
9640
    // Submit constraint
9641
0
    float aspect_ratio = 16.0f / 9.0f;
9642
0
    float fixed_step = 100.0f;
9643
0
    if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500));         // Between 100x100 and 500x500
9644
0
    if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100
9645
0
    if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0),    ImVec2(-1, FLT_MAX));      // Resize vertical + lock current width
9646
0
    if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1),    ImVec2(FLT_MAX, -1));      // Resize horizontal + lock current height
9647
0
    if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1),  ImVec2(500, -1));          // Width Between and 400 and 500
9648
0
    if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400),  ImVec2(-1, FLT_MAX));      // Height at least 400
9649
0
    if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio);   // Aspect ratio
9650
0
    if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square);                              // Always Square
9651
0
    if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step);            // Fixed Step
9652
9653
    // Submit window
9654
0
    if (!window_padding)
9655
0
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
9656
0
    const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;
9657
0
    const bool window_open = ImGui::Begin("Example: Constrained Resize", p_open, window_flags);
9658
0
    if (!window_padding)
9659
0
        ImGui::PopStyleVar();
9660
0
    if (window_open)
9661
0
    {
9662
0
        IMGUI_DEMO_MARKER("Examples/Constrained Resizing window");
9663
0
        if (ImGui::GetIO().KeyShift)
9664
0
        {
9665
            // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture)
9666
0
            ImVec2 avail_size = ImGui::GetContentRegionAvail();
9667
0
            ImVec2 pos = ImGui::GetCursorScreenPos();
9668
0
            ImGui::ColorButton("viewport", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);
9669
0
            ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));
9670
0
            ImGui::Text("%.2f x %.2f", avail_size.x, avail_size.y);
9671
0
        }
9672
0
        else
9673
0
        {
9674
0
            ImGui::Text("(Hold SHIFT to display a dummy viewport)");
9675
0
            if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();
9676
0
            if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();
9677
0
            if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); }
9678
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9679
0
            ImGui::Combo("Constraint", &type, test_desc, IM_ARRAYSIZE(test_desc));
9680
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);
9681
0
            ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100);
9682
0
            ImGui::Checkbox("Auto-resize", &auto_resize);
9683
0
            ImGui::Checkbox("Window padding", &window_padding);
9684
0
            for (int i = 0; i < display_lines; i++)
9685
0
                ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, "");
9686
0
        }
9687
0
    }
9688
0
    ImGui::End();
9689
0
}
9690
9691
//-----------------------------------------------------------------------------
9692
// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()
9693
//-----------------------------------------------------------------------------
9694
9695
// Demonstrate creating a simple static window with no decoration
9696
// + a context-menu to choose which corner of the screen to use.
9697
static void ShowExampleAppSimpleOverlay(bool* p_open)
9698
0
{
9699
0
    static int location = 0;
9700
0
    ImGuiIO& io = ImGui::GetIO();
9701
0
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
9702
0
    if (location >= 0)
9703
0
    {
9704
0
        const float PAD = 10.0f;
9705
0
        const ImGuiViewport* viewport = ImGui::GetMainViewport();
9706
0
        ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!
9707
0
        ImVec2 work_size = viewport->WorkSize;
9708
0
        ImVec2 window_pos, window_pos_pivot;
9709
0
        window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);
9710
0
        window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);
9711
0
        window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;
9712
0
        window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;
9713
0
        ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);
9714
0
        window_flags |= ImGuiWindowFlags_NoMove;
9715
0
    }
9716
0
    else if (location == -2)
9717
0
    {
9718
        // Center window
9719
0
        ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
9720
0
        window_flags |= ImGuiWindowFlags_NoMove;
9721
0
    }
9722
0
    ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background
9723
0
    if (ImGui::Begin("Example: Simple overlay", p_open, window_flags))
9724
0
    {
9725
0
        IMGUI_DEMO_MARKER("Examples/Simple Overlay");
9726
0
        ImGui::Text("Simple overlay\n" "(right-click to change position)");
9727
0
        ImGui::Separator();
9728
0
        if (ImGui::IsMousePosValid())
9729
0
            ImGui::Text("Mouse Position: (%.1f,%.1f)", io.MousePos.x, io.MousePos.y);
9730
0
        else
9731
0
            ImGui::Text("Mouse Position: <invalid>");
9732
0
        if (ImGui::BeginPopupContextWindow())
9733
0
        {
9734
0
            if (ImGui::MenuItem("Custom",       NULL, location == -1)) location = -1;
9735
0
            if (ImGui::MenuItem("Center",       NULL, location == -2)) location = -2;
9736
0
            if (ImGui::MenuItem("Top-left",     NULL, location == 0)) location = 0;
9737
0
            if (ImGui::MenuItem("Top-right",    NULL, location == 1)) location = 1;
9738
0
            if (ImGui::MenuItem("Bottom-left",  NULL, location == 2)) location = 2;
9739
0
            if (ImGui::MenuItem("Bottom-right", NULL, location == 3)) location = 3;
9740
0
            if (p_open && ImGui::MenuItem("Close")) *p_open = false;
9741
0
            ImGui::EndPopup();
9742
0
        }
9743
0
    }
9744
0
    ImGui::End();
9745
0
}
9746
9747
//-----------------------------------------------------------------------------
9748
// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()
9749
//-----------------------------------------------------------------------------
9750
9751
// Demonstrate creating a window covering the entire screen/viewport
9752
static void ShowExampleAppFullscreen(bool* p_open)
9753
0
{
9754
0
    static bool use_work_area = true;
9755
0
    static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;
9756
9757
    // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)
9758
    // Based on your use case you may want one or the other.
9759
0
    const ImGuiViewport* viewport = ImGui::GetMainViewport();
9760
0
    ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);
9761
0
    ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);
9762
9763
0
    if (ImGui::Begin("Example: Fullscreen window", p_open, flags))
9764
0
    {
9765
0
        ImGui::Checkbox("Use work area instead of main area", &use_work_area);
9766
0
        ImGui::SameLine();
9767
0
        HelpMarker("Main Area = entire viewport,\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\n\nEnable the main-menu bar in Examples menu to see the difference.");
9768
9769
0
        ImGui::CheckboxFlags("ImGuiWindowFlags_NoBackground", &flags, ImGuiWindowFlags_NoBackground);
9770
0
        ImGui::CheckboxFlags("ImGuiWindowFlags_NoDecoration", &flags, ImGuiWindowFlags_NoDecoration);
9771
0
        ImGui::Indent();
9772
0
        ImGui::CheckboxFlags("ImGuiWindowFlags_NoTitleBar", &flags, ImGuiWindowFlags_NoTitleBar);
9773
0
        ImGui::CheckboxFlags("ImGuiWindowFlags_NoCollapse", &flags, ImGuiWindowFlags_NoCollapse);
9774
0
        ImGui::CheckboxFlags("ImGuiWindowFlags_NoScrollbar", &flags, ImGuiWindowFlags_NoScrollbar);
9775
0
        ImGui::Unindent();
9776
9777
0
        if (p_open && ImGui::Button("Close this window"))
9778
0
            *p_open = false;
9779
0
    }
9780
0
    ImGui::End();
9781
0
}
9782
9783
//-----------------------------------------------------------------------------
9784
// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()
9785
//-----------------------------------------------------------------------------
9786
9787
// Demonstrate the use of "##" and "###" in identifiers to manipulate ID generation.
9788
// This applies to all regular items as well.
9789
// Read FAQ section "How can I have multiple widgets with the same label?" for details.
9790
static void ShowExampleAppWindowTitles(bool*)
9791
0
{
9792
0
    const ImGuiViewport* viewport = ImGui::GetMainViewport();
9793
0
    const ImVec2 base_pos = viewport->Pos;
9794
9795
    // By default, Windows are uniquely identified by their title.
9796
    // You can use the "##" and "###" markers to manipulate the display/ID.
9797
9798
    // Using "##" to display same title but have unique identifier.
9799
0
    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver);
9800
0
    ImGui::Begin("Same title as another window##1");
9801
0
    IMGUI_DEMO_MARKER("Examples/Manipulating window titles");
9802
0
    ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique.");
9803
0
    ImGui::End();
9804
9805
0
    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver);
9806
0
    ImGui::Begin("Same title as another window##2");
9807
0
    ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique.");
9808
0
    ImGui::End();
9809
9810
    // Using "###" to display a changing title but keep a static identifier "AnimatedTitle"
9811
0
    char buf[128];
9812
0
    sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());
9813
0
    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver);
9814
0
    ImGui::Begin(buf);
9815
0
    ImGui::Text("This window has a changing title.");
9816
0
    ImGui::End();
9817
0
}
9818
9819
//-----------------------------------------------------------------------------
9820
// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()
9821
//-----------------------------------------------------------------------------
9822
9823
// Add a |_| looking shape
9824
static void PathConcaveShape(ImDrawList* draw_list, float x, float y, float sz)
9825
0
{
9826
0
    const ImVec2 pos_norms[] = { { 0.0f, 0.0f }, { 0.3f, 0.0f }, { 0.3f, 0.7f }, { 0.7f, 0.7f }, { 0.7f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } };
9827
0
    for (const ImVec2& p : pos_norms)
9828
0
        draw_list->PathLineTo(ImVec2(x + 0.5f + (int)(sz * p.x), y + 0.5f + (int)(sz * p.y)));
9829
0
}
9830
9831
// Demonstrate using the low-level ImDrawList to draw custom shapes.
9832
static void ShowExampleAppCustomRendering(bool* p_open)
9833
0
{
9834
0
    if (!ImGui::Begin("Example: Custom rendering", p_open))
9835
0
    {
9836
0
        ImGui::End();
9837
0
        return;
9838
0
    }
9839
0
    IMGUI_DEMO_MARKER("Examples/Custom Rendering");
9840
9841
    // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of
9842
    // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your
9843
    // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not
9844
    // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!
9845
9846
0
    if (ImGui::BeginTabBar("##TabBar"))
9847
0
    {
9848
0
        if (ImGui::BeginTabItem("Primitives"))
9849
0
        {
9850
0
            ImGui::PushItemWidth(-ImGui::GetFontSize() * 15);
9851
0
            ImDrawList* draw_list = ImGui::GetWindowDrawList();
9852
9853
            // Draw gradients
9854
            // (note that those are currently exacerbating our sRGB/Linear issues)
9855
            // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..
9856
0
            ImGui::Text("Gradients");
9857
0
            ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());
9858
0
            {
9859
0
                ImVec2 p0 = ImGui::GetCursorScreenPos();
9860
0
                ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9861
0
                ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));
9862
0
                ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));
9863
0
                draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
9864
0
                ImGui::InvisibleButton("##gradient1", gradient_size);
9865
0
            }
9866
0
            {
9867
0
                ImVec2 p0 = ImGui::GetCursorScreenPos();
9868
0
                ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);
9869
0
                ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));
9870
0
                ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));
9871
0
                draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);
9872
0
                ImGui::InvisibleButton("##gradient2", gradient_size);
9873
0
            }
9874
9875
            // Draw a bunch of primitives
9876
0
            ImGui::Text("All primitives");
9877
0
            static float sz = 36.0f;
9878
0
            static float thickness = 3.0f;
9879
0
            static int ngon_sides = 6;
9880
0
            static bool circle_segments_override = false;
9881
0
            static int circle_segments_override_v = 12;
9882
0
            static bool curve_segments_override = false;
9883
0
            static int curve_segments_override_v = 8;
9884
0
            static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
9885
0
            ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 100.0f, "%.0f");
9886
0
            ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f");
9887
0
            ImGui::SliderInt("N-gon sides", &ngon_sides, 3, 12);
9888
0
            ImGui::Checkbox("##circlesegmentoverride", &circle_segments_override);
9889
0
            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
9890
0
            circle_segments_override |= ImGui::SliderInt("Circle segments override", &circle_segments_override_v, 3, 40);
9891
0
            ImGui::Checkbox("##curvessegmentoverride", &curve_segments_override);
9892
0
            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
9893
0
            curve_segments_override |= ImGui::SliderInt("Curves segments override", &curve_segments_override_v, 3, 40);
9894
0
            ImGui::ColorEdit4("Color", &colf.x);
9895
9896
0
            const ImVec2 p = ImGui::GetCursorScreenPos();
9897
0
            const ImU32 col = ImColor(colf);
9898
0
            const float spacing = 10.0f;
9899
0
            const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;
9900
0
            const float rounding = sz / 5.0f;
9901
0
            const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;
9902
0
            const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;
9903
0
            const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves
9904
0
            const ImVec2 cp4[4] = { ImVec2(0.0f, 0.0f), ImVec2(sz * 1.3f, sz * 0.3f), ImVec2(sz - sz * 1.3f, sz - sz * 0.3f), ImVec2(sz, sz) };
9905
9906
0
            float x = p.x + 4.0f;
9907
0
            float y = p.y + 4.0f;
9908
0
            for (int n = 0; n < 2; n++)
9909
0
            {
9910
                // First line uses a thickness of 1.0f, second line uses the configurable thickness
9911
0
                float th = (n == 0) ? 1.0f : thickness;
9912
0
                draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th);                 x += sz + spacing;  // N-gon
9913
0
                draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th);          x += sz + spacing;  // Circle
9914
0
                draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), ImVec2(sz*0.5f, sz*0.3f), col, -0.3f, circle_segments, th); x += sz + spacing;  // Ellipse
9915
0
                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th);          x += sz + spacing;  // Square
9916
0
                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th);      x += sz + spacing;  // Square with all rounded corners
9917
0
                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th);         x += sz + spacing;  // Square with two rounded corners
9918
0
                draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing;  // Triangle
9919
                //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle
9920
0
                PathConcaveShape(draw_list, x, y, sz); draw_list->PathStroke(col, ImDrawFlags_Closed, th);          x += sz + spacing;  // Concave Shape
9921
                //draw_list->AddPolyline(concave_shape, IM_ARRAYSIZE(concave_shape), col, ImDrawFlags_Closed, th);
9922
0
                draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th);                                       x += sz + spacing;  // Horizontal line (note: drawing a filled rectangle will be faster!)
9923
0
                draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th);                                       x += spacing;       // Vertical line (note: drawing a filled rectangle will be faster!)
9924
0
                draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th);                                  x += sz + spacing;  // Diagonal line
9925
9926
                // Path
9927
0
                draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f);
9928
0
                draw_list->PathStroke(col, ImDrawFlags_None, th);
9929
0
                x += sz + spacing;
9930
9931
                // Quadratic Bezier Curve (3 control points)
9932
0
                draw_list->AddBezierQuadratic(ImVec2(x + cp3[0].x, y + cp3[0].y), ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), col, th, curve_segments);
9933
0
                x += sz + spacing;
9934
9935
                // Cubic Bezier Curve (4 control points)
9936
0
                draw_list->AddBezierCubic(ImVec2(x + cp4[0].x, y + cp4[0].y), ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), col, th, curve_segments);
9937
9938
0
                x = p.x + 4;
9939
0
                y += sz + spacing;
9940
0
            }
9941
9942
            // Filled shapes
9943
0
            draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides);             x += sz + spacing;  // N-gon
9944
0
            draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments);      x += sz + spacing;  // Circle
9945
0
            draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), ImVec2(sz * 0.5f, sz * 0.3f), col, -0.3f, circle_segments); x += sz + spacing;// Ellipse
9946
0
            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col);                                    x += sz + spacing;  // Square
9947
0
            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f);                             x += sz + spacing;  // Square with all rounded corners
9948
0
            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br);              x += sz + spacing;  // Square with two rounded corners
9949
0
            draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col);  x += sz + spacing;  // Triangle
9950
            //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle
9951
0
            PathConcaveShape(draw_list, x, y, sz); draw_list->PathFillConcave(col);                                 x += sz + spacing;  // Concave shape
9952
0
            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col);                             x += sz + spacing;  // Horizontal line (faster than AddLine, but only handle integer thickness)
9953
0
            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col);                             x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness)
9954
0
            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col);                                      x += sz;            // Pixel (faster than AddLine)
9955
9956
            // Path
9957
0
            draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f);
9958
0
            draw_list->PathFillConvex(col);
9959
0
            x += sz + spacing;
9960
9961
            // Quadratic Bezier Curve (3 control points)
9962
0
            draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y));
9963
0
            draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments);
9964
0
            draw_list->PathFillConvex(col);
9965
0
            x += sz + spacing;
9966
9967
0
            draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));
9968
0
            x += sz + spacing;
9969
9970
0
            ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f));
9971
0
            ImGui::PopItemWidth();
9972
0
            ImGui::EndTabItem();
9973
0
        }
9974
9975
0
        if (ImGui::BeginTabItem("Canvas"))
9976
0
        {
9977
0
            static ImVector<ImVec2> points;
9978
0
            static ImVec2 scrolling(0.0f, 0.0f);
9979
0
            static bool opt_enable_grid = true;
9980
0
            static bool opt_enable_context_menu = true;
9981
0
            static bool adding_line = false;
9982
9983
0
            ImGui::Checkbox("Enable grid", &opt_enable_grid);
9984
0
            ImGui::Checkbox("Enable context menu", &opt_enable_context_menu);
9985
0
            ImGui::Text("Mouse Left: drag to add lines,\nMouse Right: drag to scroll, click for context menu.");
9986
9987
            // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.
9988
            // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.
9989
            // To use a child window instead we could use, e.g:
9990
            //      ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));      // Disable padding
9991
            //      ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255));  // Set a background color
9992
            //      ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
9993
            //      ImGui::PopStyleColor();
9994
            //      ImGui::PopStyleVar();
9995
            //      [...]
9996
            //      ImGui::EndChild();
9997
9998
            // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()
9999
0
            ImVec2 canvas_p0 = ImGui::GetCursorScreenPos();      // ImDrawList API uses screen coordinates!
10000
0
            ImVec2 canvas_sz = ImGui::GetContentRegionAvail();   // Resize canvas to what's available
10001
0
            if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;
10002
0
            if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;
10003
0
            ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);
10004
10005
            // Draw border and background color
10006
0
            ImGuiIO& io = ImGui::GetIO();
10007
0
            ImDrawList* draw_list = ImGui::GetWindowDrawList();
10008
0
            draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));
10009
0
            draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));
10010
10011
            // This will catch our interactions
10012
0
            ImGui::InvisibleButton("canvas", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);
10013
0
            const bool is_hovered = ImGui::IsItemHovered(); // Hovered
10014
0
            const bool is_active = ImGui::IsItemActive();   // Held
10015
0
            const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin
10016
0
            const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);
10017
10018
            // Add first and second point
10019
0
            if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
10020
0
            {
10021
0
                points.push_back(mouse_pos_in_canvas);
10022
0
                points.push_back(mouse_pos_in_canvas);
10023
0
                adding_line = true;
10024
0
            }
10025
0
            if (adding_line)
10026
0
            {
10027
0
                points.back() = mouse_pos_in_canvas;
10028
0
                if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))
10029
0
                    adding_line = false;
10030
0
            }
10031
10032
            // Pan (we use a zero mouse threshold when there's no context menu)
10033
            // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.
10034
0
            const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;
10035
0
            if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))
10036
0
            {
10037
0
                scrolling.x += io.MouseDelta.x;
10038
0
                scrolling.y += io.MouseDelta.y;
10039
0
            }
10040
10041
            // Context menu (under default mouse threshold)
10042
0
            ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);
10043
0
            if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)
10044
0
                ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
10045
0
            if (ImGui::BeginPopup("context"))
10046
0
            {
10047
0
                if (adding_line)
10048
0
                    points.resize(points.size() - 2);
10049
0
                adding_line = false;
10050
0
                if (ImGui::MenuItem("Remove one", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); }
10051
0
                if (ImGui::MenuItem("Remove all", NULL, false, points.Size > 0)) { points.clear(); }
10052
0
                ImGui::EndPopup();
10053
0
            }
10054
10055
            // Draw grid + all lines in the canvas
10056
0
            draw_list->PushClipRect(canvas_p0, canvas_p1, true);
10057
0
            if (opt_enable_grid)
10058
0
            {
10059
0
                const float GRID_STEP = 64.0f;
10060
0
                for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)
10061
0
                    draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));
10062
0
                for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)
10063
0
                    draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));
10064
0
            }
10065
0
            for (int n = 0; n < points.Size; n += 2)
10066
0
                draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);
10067
0
            draw_list->PopClipRect();
10068
10069
0
            ImGui::EndTabItem();
10070
0
        }
10071
10072
0
        if (ImGui::BeginTabItem("BG/FG draw lists"))
10073
0
        {
10074
0
            static bool draw_bg = true;
10075
0
            static bool draw_fg = true;
10076
0
            ImGui::Checkbox("Draw in Background draw list", &draw_bg);
10077
0
            ImGui::SameLine(); HelpMarker("The Background draw list will be rendered below every Dear ImGui windows.");
10078
0
            ImGui::Checkbox("Draw in Foreground draw list", &draw_fg);
10079
0
            ImGui::SameLine(); HelpMarker("The Foreground draw list will be rendered over every Dear ImGui windows.");
10080
0
            ImVec2 window_pos = ImGui::GetWindowPos();
10081
0
            ImVec2 window_size = ImGui::GetWindowSize();
10082
0
            ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);
10083
0
            if (draw_bg)
10084
0
                ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4);
10085
0
            if (draw_fg)
10086
0
                ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10);
10087
0
            ImGui::EndTabItem();
10088
0
        }
10089
10090
        // Demonstrate out-of-order rendering via channels splitting
10091
        // We use functions in ImDrawList as each draw list contains a convenience splitter,
10092
        // but you can also instantiate your own ImDrawListSplitter if you need to nest them.
10093
0
        if (ImGui::BeginTabItem("Draw Channels"))
10094
0
        {
10095
0
            ImDrawList* draw_list = ImGui::GetWindowDrawList();
10096
0
            {
10097
0
                ImGui::Text("Blue shape is drawn first: appears in back");
10098
0
                ImGui::Text("Red shape is drawn after: appears in front");
10099
0
                ImVec2 p0 = ImGui::GetCursorScreenPos();
10100
0
                draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10101
0
                draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10102
0
                ImGui::Dummy(ImVec2(75, 75));
10103
0
            }
10104
0
            ImGui::Separator();
10105
0
            {
10106
0
                ImGui::Text("Blue shape is drawn first, into channel 1: appears in front");
10107
0
                ImGui::Text("Red shape is drawn after, into channel 0: appears in back");
10108
0
                ImVec2 p1 = ImGui::GetCursorScreenPos();
10109
10110
                // Create 2 channels and draw a Blue shape THEN a Red shape.
10111
                // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.
10112
0
                draw_list->ChannelsSplit(2);
10113
0
                draw_list->ChannelsSetCurrent(1);
10114
0
                draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue
10115
0
                draw_list->ChannelsSetCurrent(0);
10116
0
                draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red
10117
10118
                // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.
10119
                // This works by copying draw indices only (vertices are not copied).
10120
0
                draw_list->ChannelsMerge();
10121
0
                ImGui::Dummy(ImVec2(75, 75));
10122
0
                ImGui::Text("After reordering, contents of channel 0 appears below channel 1.");
10123
0
            }
10124
0
            ImGui::EndTabItem();
10125
0
        }
10126
10127
0
        ImGui::EndTabBar();
10128
0
    }
10129
10130
0
    ImGui::End();
10131
0
}
10132
10133
//-----------------------------------------------------------------------------
10134
// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()
10135
//-----------------------------------------------------------------------------
10136
10137
// Simplified structure to mimic a Document model
10138
struct MyDocument
10139
{
10140
    char        Name[32];   // Document title
10141
    int         UID;        // Unique ID (necessary as we can change title)
10142
    bool        Open;       // Set when open (we keep an array of all available documents to simplify demo code!)
10143
    bool        OpenPrev;   // Copy of Open from last update.
10144
    bool        Dirty;      // Set when the document has been modified
10145
    ImVec4      Color;      // An arbitrary variable associated to the document
10146
10147
    MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
10148
0
    {
10149
0
        UID = uid;
10150
0
        snprintf(Name, sizeof(Name), "%s", name);
10151
0
        Open = OpenPrev = open;
10152
0
        Dirty = false;
10153
0
        Color = color;
10154
0
    }
10155
0
    void DoOpen()       { Open = true; }
10156
0
    void DoForceClose() { Open = false; Dirty = false; }
10157
0
    void DoSave()       { Dirty = false; }
10158
};
10159
10160
struct ExampleAppDocuments
10161
{
10162
    ImVector<MyDocument>    Documents;
10163
    ImVector<MyDocument*>   CloseQueue;
10164
    MyDocument*             RenamingDoc = NULL;
10165
    bool                    RenamingStarted = false;
10166
10167
    ExampleAppDocuments()
10168
0
    {
10169
0
        Documents.push_back(MyDocument(0, "Lettuce",             true,  ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
10170
0
        Documents.push_back(MyDocument(1, "Eggplant",            true,  ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
10171
0
        Documents.push_back(MyDocument(2, "Carrot",              true,  ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
10172
0
        Documents.push_back(MyDocument(3, "Tomato",              false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
10173
0
        Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));
10174
0
        Documents.push_back(MyDocument(5, "Some Document",       false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));
10175
0
    }
10176
10177
    // As we allow to change document name, we append a never-changing document ID so tabs are stable
10178
    void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)
10179
0
    {
10180
0
        snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID);
10181
0
    }
10182
10183
    // Display placeholder contents for the Document
10184
    void DisplayDocContents(MyDocument* doc)
10185
0
    {
10186
0
        ImGui::PushID(doc);
10187
0
        ImGui::Text("Document \"%s\"", doc->Name);
10188
0
        ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);
10189
0
        ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
10190
0
        ImGui::PopStyleColor();
10191
10192
0
        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip);
10193
0
        if (ImGui::Button("Rename.."))
10194
0
        {
10195
0
            RenamingDoc = doc;
10196
0
            RenamingStarted = true;
10197
0
        }
10198
0
        ImGui::SameLine();
10199
10200
0
        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip);
10201
0
        if (ImGui::Button("Modify"))
10202
0
            doc->Dirty = true;
10203
10204
0
        ImGui::SameLine();
10205
0
        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip);
10206
0
        if (ImGui::Button("Save"))
10207
0
            doc->DoSave();
10208
10209
0
        ImGui::SameLine();
10210
0
        ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip);
10211
0
        if (ImGui::Button("Close"))
10212
0
            CloseQueue.push_back(doc);
10213
0
        ImGui::ColorEdit3("color", &doc->Color.x);  // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
10214
0
        ImGui::PopID();
10215
0
    }
10216
10217
    // Display context menu for the Document
10218
    void DisplayDocContextMenu(MyDocument* doc)
10219
0
    {
10220
0
        if (!ImGui::BeginPopupContextItem())
10221
0
            return;
10222
10223
0
        char buf[256];
10224
0
        sprintf(buf, "Save %s", doc->Name);
10225
0
        if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open))
10226
0
            doc->DoSave();
10227
0
        if (ImGui::MenuItem("Rename...", "Ctrl+R", false, doc->Open))
10228
0
            RenamingDoc = doc;
10229
0
        if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open))
10230
0
            CloseQueue.push_back(doc);
10231
0
        ImGui::EndPopup();
10232
0
    }
10233
10234
    // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
10235
    // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
10236
    // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
10237
    // the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has
10238
    // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
10239
    // give the impression of a flicker for one frame.
10240
    // We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
10241
    // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
10242
    void NotifyOfDocumentsClosedElsewhere()
10243
0
    {
10244
0
        for (MyDocument& doc : Documents)
10245
0
        {
10246
0
            if (!doc.Open && doc.OpenPrev)
10247
0
                ImGui::SetTabItemClosed(doc.Name);
10248
0
            doc.OpenPrev = doc.Open;
10249
0
        }
10250
0
    }
10251
};
10252
10253
void ShowExampleAppDocuments(bool* p_open)
10254
0
{
10255
0
    static ExampleAppDocuments app;
10256
10257
    // Options
10258
0
    static bool opt_reorderable = true;
10259
0
    static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;
10260
10261
0
    bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar);
10262
0
    if (!window_contents_visible)
10263
0
    {
10264
0
        ImGui::End();
10265
0
        return;
10266
0
    }
10267
10268
    // Menu
10269
0
    if (ImGui::BeginMenuBar())
10270
0
    {
10271
0
        if (ImGui::BeginMenu("File"))
10272
0
        {
10273
0
            int open_count = 0;
10274
0
            for (MyDocument& doc : app.Documents)
10275
0
                open_count += doc.Open ? 1 : 0;
10276
10277
0
            if (ImGui::BeginMenu("Open", open_count < app.Documents.Size))
10278
0
            {
10279
0
                for (MyDocument& doc : app.Documents)
10280
0
                    if (!doc.Open && ImGui::MenuItem(doc.Name))
10281
0
                        doc.DoOpen();
10282
0
                ImGui::EndMenu();
10283
0
            }
10284
0
            if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0))
10285
0
                for (MyDocument& doc : app.Documents)
10286
0
                    app.CloseQueue.push_back(&doc);
10287
0
            if (ImGui::MenuItem("Exit") && p_open)
10288
0
                *p_open = false;
10289
0
            ImGui::EndMenu();
10290
0
        }
10291
0
        ImGui::EndMenuBar();
10292
0
    }
10293
10294
    // [Debug] List documents with one checkbox for each
10295
0
    for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)
10296
0
    {
10297
0
        MyDocument& doc = app.Documents[doc_n];
10298
0
        if (doc_n > 0)
10299
0
            ImGui::SameLine();
10300
0
        ImGui::PushID(&doc);
10301
0
        if (ImGui::Checkbox(doc.Name, &doc.Open))
10302
0
            if (!doc.Open)
10303
0
                doc.DoForceClose();
10304
0
        ImGui::PopID();
10305
0
    }
10306
10307
0
    ImGui::Separator();
10308
10309
    // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.
10310
    // They have multiple effects:
10311
    // - Display a dot next to the title.
10312
    // - Tab is selected when clicking the X close button.
10313
    // - Closure is not assumed (will wait for user to stop submitting the tab).
10314
    //   Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.
10315
    //   We need to assume closure by default otherwise waiting for "lack of submission" on the next frame would leave an empty
10316
    //   hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.
10317
    //   The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.
10318
10319
    // Submit Tab Bar and Tabs
10320
0
    {
10321
0
        ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);
10322
0
        tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline;
10323
0
        if (ImGui::BeginTabBar("##tabs", tab_bar_flags))
10324
0
        {
10325
0
            if (opt_reorderable)
10326
0
                app.NotifyOfDocumentsClosedElsewhere();
10327
10328
            // [DEBUG] Stress tests
10329
            //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1;            // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
10330
            //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name);  // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..
10331
10332
            // Submit Tabs
10333
0
            for (MyDocument& doc : app.Documents)
10334
0
            {
10335
0
                if (!doc.Open)
10336
0
                    continue;
10337
10338
                // As we allow to change document name, we append a never-changing document id so tabs are stable
10339
0
                char doc_name_buf[64];
10340
0
                app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf));
10341
0
                ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
10342
0
                bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags);
10343
10344
                // Cancel attempt to close when unsaved add to save queue so we can display a popup.
10345
0
                if (!doc.Open && doc.Dirty)
10346
0
                {
10347
0
                    doc.Open = true;
10348
0
                    app.CloseQueue.push_back(&doc);
10349
0
                }
10350
10351
0
                app.DisplayDocContextMenu(&doc);
10352
0
                if (visible)
10353
0
                {
10354
0
                    app.DisplayDocContents(&doc);
10355
0
                    ImGui::EndTabItem();
10356
0
                }
10357
0
            }
10358
10359
0
            ImGui::EndTabBar();
10360
0
        }
10361
0
    }
10362
10363
    // Display renaming UI
10364
0
    if (app.RenamingDoc != NULL)
10365
0
    {
10366
0
        if (app.RenamingStarted)
10367
0
            ImGui::OpenPopup("Rename");
10368
0
        if (ImGui::BeginPopup("Rename"))
10369
0
        {
10370
0
            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);
10371
0
            if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue))
10372
0
            {
10373
0
                ImGui::CloseCurrentPopup();
10374
0
                app.RenamingDoc = NULL;
10375
0
            }
10376
0
            if (app.RenamingStarted)
10377
0
                ImGui::SetKeyboardFocusHere(-1);
10378
0
            ImGui::EndPopup();
10379
0
        }
10380
0
        else
10381
0
        {
10382
0
            app.RenamingDoc = NULL;
10383
0
        }
10384
0
        app.RenamingStarted = false;
10385
0
    }
10386
10387
    // Display closing confirmation UI
10388
0
    if (!app.CloseQueue.empty())
10389
0
    {
10390
0
        int close_queue_unsaved_documents = 0;
10391
0
        for (int n = 0; n < app.CloseQueue.Size; n++)
10392
0
            if (app.CloseQueue[n]->Dirty)
10393
0
                close_queue_unsaved_documents++;
10394
10395
0
        if (close_queue_unsaved_documents == 0)
10396
0
        {
10397
            // Close documents when all are unsaved
10398
0
            for (int n = 0; n < app.CloseQueue.Size; n++)
10399
0
                app.CloseQueue[n]->DoForceClose();
10400
0
            app.CloseQueue.clear();
10401
0
        }
10402
0
        else
10403
0
        {
10404
0
            if (!ImGui::IsPopupOpen("Save?"))
10405
0
                ImGui::OpenPopup("Save?");
10406
0
            if (ImGui::BeginPopupModal("Save?", NULL, ImGuiWindowFlags_AlwaysAutoResize))
10407
0
            {
10408
0
                ImGui::Text("Save change to the following items?");
10409
0
                float item_height = ImGui::GetTextLineHeightWithSpacing();
10410
0
                if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle))
10411
0
                    for (MyDocument* doc : app.CloseQueue)
10412
0
                        if (doc->Dirty)
10413
0
                            ImGui::Text("%s", doc->Name);
10414
0
                ImGui::EndChild();
10415
10416
0
                ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
10417
0
                if (ImGui::Button("Yes", button_size))
10418
0
                {
10419
0
                    for (MyDocument* doc : app.CloseQueue)
10420
0
                    {
10421
0
                        if (doc->Dirty)
10422
0
                            doc->DoSave();
10423
0
                        doc->DoForceClose();
10424
0
                    }
10425
0
                    app.CloseQueue.clear();
10426
0
                    ImGui::CloseCurrentPopup();
10427
0
                }
10428
0
                ImGui::SameLine();
10429
0
                if (ImGui::Button("No", button_size))
10430
0
                {
10431
0
                    for (MyDocument* doc : app.CloseQueue)
10432
0
                        doc->DoForceClose();
10433
0
                    app.CloseQueue.clear();
10434
0
                    ImGui::CloseCurrentPopup();
10435
0
                }
10436
0
                ImGui::SameLine();
10437
0
                if (ImGui::Button("Cancel", button_size))
10438
0
                {
10439
0
                    app.CloseQueue.clear();
10440
0
                    ImGui::CloseCurrentPopup();
10441
0
                }
10442
0
                ImGui::EndPopup();
10443
0
            }
10444
0
        }
10445
0
    }
10446
10447
0
    ImGui::End();
10448
0
}
10449
10450
//-----------------------------------------------------------------------------
10451
// [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser()
10452
//-----------------------------------------------------------------------------
10453
10454
//#include "imgui_internal.h" // NavMoveRequestTryWrapping()
10455
10456
struct ExampleAsset
10457
{
10458
    ImGuiID ID;
10459
    int     Type;
10460
10461
0
    ExampleAsset(ImGuiID id, int type) { ID = id; Type = type; }
10462
10463
    static const ImGuiTableSortSpecs* s_current_sort_specs;
10464
10465
    static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, ExampleAsset* items, int items_count)
10466
0
    {
10467
0
        s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.
10468
0
        if (items_count > 1)
10469
0
            qsort(items, (size_t)items_count, sizeof(items[0]), ExampleAsset::CompareWithSortSpecs);
10470
0
        s_current_sort_specs = NULL;
10471
0
    }
10472
10473
    // Compare function to be used by qsort()
10474
    static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)
10475
0
    {
10476
0
        const ExampleAsset* a = (const ExampleAsset*)lhs;
10477
0
        const ExampleAsset* b = (const ExampleAsset*)rhs;
10478
0
        for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)
10479
0
        {
10480
0
            const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];
10481
0
            int delta = 0;
10482
0
            if (sort_spec->ColumnIndex == 0)
10483
0
                delta = ((int)a->ID - (int)b->ID);
10484
0
            else if (sort_spec->ColumnIndex == 1)
10485
0
                delta = (a->Type - b->Type);
10486
0
            if (delta > 0)
10487
0
                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;
10488
0
            if (delta < 0)
10489
0
                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;
10490
0
        }
10491
0
        return ((int)a->ID - (int)b->ID);
10492
0
    }
10493
};
10494
const ImGuiTableSortSpecs* ExampleAsset::s_current_sort_specs = NULL;
10495
10496
struct ExampleAssetsBrowser
10497
{
10498
    // Options
10499
    bool            ShowTypeOverlay = true;
10500
    bool            AllowSorting = true;
10501
    bool            AllowDragUnselected = false;
10502
    bool            AllowBoxSelect = true;
10503
    float           IconSize = 32.0f;
10504
    int             IconSpacing = 10;
10505
    int             IconHitSpacing = 4;         // Increase hit-spacing if you want to make it possible to clear or box-select from gaps. Some spacing is required to able to amend with Shift+box-select. Value is small in Explorer.
10506
    bool            StretchSpacing = true;
10507
10508
    // State
10509
    ImVector<ExampleAsset> Items;               // Our items
10510
    ExampleSelectionWithDeletion Selection;     // Our selection (ImGuiSelectionBasicStorage + helper funcs to handle deletion)
10511
    ImGuiID         NextItemId = 0;             // Unique identifier when creating new items
10512
    bool            RequestDelete = false;      // Deferred deletion request
10513
    bool            RequestSort = false;        // Deferred sort request
10514
    float           ZoomWheelAccum = 0.0f;      // Mouse wheel accumulator to handle smooth wheels better
10515
10516
    // Calculated sizes for layout, output of UpdateLayoutSizes(). Could be locals but our code is simpler this way.
10517
    ImVec2          LayoutItemSize;
10518
    ImVec2          LayoutItemStep;             // == LayoutItemSize + LayoutItemSpacing
10519
    float           LayoutItemSpacing = 0.0f;
10520
    float           LayoutSelectableSpacing = 0.0f;
10521
    float           LayoutOuterPadding = 0.0f;
10522
    int             LayoutColumnCount = 0;
10523
    int             LayoutLineCount = 0;
10524
10525
    // Functions
10526
    ExampleAssetsBrowser()
10527
0
    {
10528
0
        AddItems(10000);
10529
0
    }
10530
    void AddItems(int count)
10531
0
    {
10532
0
        if (Items.Size == 0)
10533
0
            NextItemId = 0;
10534
0
        Items.reserve(Items.Size + count);
10535
0
        for (int n = 0; n < count; n++, NextItemId++)
10536
0
            Items.push_back(ExampleAsset(NextItemId, (NextItemId % 20) < 15 ? 0 : (NextItemId % 20) < 18 ? 1 : 2));
10537
0
        RequestSort = true;
10538
0
    }
10539
    void ClearItems()
10540
0
    {
10541
0
        Items.clear();
10542
0
        Selection.Clear();
10543
0
    }
10544
10545
    // Logic would be written in the main code BeginChild() and outputting to local variables.
10546
    // We extracted it into a function so we can call it easily from multiple places.
10547
    void UpdateLayoutSizes(float avail_width)
10548
0
    {
10549
        // Layout: when not stretching: allow extending into right-most spacing.
10550
0
        LayoutItemSpacing = (float)IconSpacing;
10551
0
        if (StretchSpacing == false)
10552
0
            avail_width += floorf(LayoutItemSpacing * 0.5f);
10553
10554
        // Layout: calculate number of icon per line and number of lines
10555
0
        LayoutItemSize = ImVec2(floorf(IconSize), floorf(IconSize));
10556
0
        LayoutColumnCount = IM_MAX((int)(avail_width / (LayoutItemSize.x + LayoutItemSpacing)), 1);
10557
0
        LayoutLineCount = (Items.Size + LayoutColumnCount - 1) / LayoutColumnCount;
10558
10559
        // Layout: when stretching: allocate remaining space to more spacing. Round before division, so item_spacing may be non-integer.
10560
0
        if (StretchSpacing && LayoutColumnCount > 1)
10561
0
            LayoutItemSpacing = floorf(avail_width - LayoutItemSize.x * LayoutColumnCount) / LayoutColumnCount;
10562
10563
0
        LayoutItemStep = ImVec2(LayoutItemSize.x + LayoutItemSpacing, LayoutItemSize.y + LayoutItemSpacing);
10564
0
        LayoutSelectableSpacing = IM_MAX(floorf(LayoutItemSpacing) - IconHitSpacing, 0.0f);
10565
0
        LayoutOuterPadding = floorf(LayoutItemSpacing * 0.5f);
10566
0
    }
10567
10568
    void Draw(const char* title, bool* p_open)
10569
0
    {
10570
0
        ImGui::SetNextWindowSize(ImVec2(IconSize * 25, IconSize * 15), ImGuiCond_FirstUseEver);
10571
0
        if (!ImGui::Begin(title, p_open, ImGuiWindowFlags_MenuBar))
10572
0
        {
10573
0
            ImGui::End();
10574
0
            return;
10575
0
        }
10576
10577
        // Menu bar
10578
0
        if (ImGui::BeginMenuBar())
10579
0
        {
10580
0
            if (ImGui::BeginMenu("File"))
10581
0
            {
10582
0
                if (ImGui::MenuItem("Add 10000 items"))
10583
0
                    AddItems(10000);
10584
0
                if (ImGui::MenuItem("Clear items"))
10585
0
                    ClearItems();
10586
0
                ImGui::Separator();
10587
0
                if (ImGui::MenuItem("Close", NULL, false, p_open != NULL))
10588
0
                    *p_open = false;
10589
0
                ImGui::EndMenu();
10590
0
            }
10591
0
            if (ImGui::BeginMenu("Edit"))
10592
0
            {
10593
0
                if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0))
10594
0
                    RequestDelete = true;
10595
0
                ImGui::EndMenu();
10596
0
            }
10597
0
            if (ImGui::BeginMenu("Options"))
10598
0
            {
10599
0
                ImGui::PushItemWidth(ImGui::GetFontSize() * 10);
10600
10601
0
                ImGui::SeparatorText("Contents");
10602
0
                ImGui::Checkbox("Show Type Overlay", &ShowTypeOverlay);
10603
0
                ImGui::Checkbox("Allow Sorting", &AllowSorting);
10604
10605
0
                ImGui::SeparatorText("Selection Behavior");
10606
0
                ImGui::Checkbox("Allow dragging unselected item", &AllowDragUnselected);
10607
0
                ImGui::Checkbox("Allow box-selection", &AllowBoxSelect);
10608
10609
0
                ImGui::SeparatorText("Layout");
10610
0
                ImGui::SliderFloat("Icon Size", &IconSize, 16.0f, 128.0f, "%.0f");
10611
0
                ImGui::SameLine(); HelpMarker("Use CTRL+Wheel to zoom");
10612
0
                ImGui::SliderInt("Icon Spacing", &IconSpacing, 0, 32);
10613
0
                ImGui::SliderInt("Icon Hit Spacing", &IconHitSpacing, 0, 32);
10614
0
                ImGui::Checkbox("Stretch Spacing", &StretchSpacing);
10615
0
                ImGui::PopItemWidth();
10616
0
                ImGui::EndMenu();
10617
0
            }
10618
0
            ImGui::EndMenuBar();
10619
0
        }
10620
10621
        // Show a table with ONLY one header row to showcase the idea/possibility of using this to provide a sorting UI
10622
0
        if (AllowSorting)
10623
0
        {
10624
0
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
10625
0
            ImGuiTableFlags table_flags_for_sort_specs = ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders;
10626
0
            if (ImGui::BeginTable("for_sort_specs_only", 2, table_flags_for_sort_specs, ImVec2(0.0f, ImGui::GetFrameHeight())))
10627
0
            {
10628
0
                ImGui::TableSetupColumn("Index");
10629
0
                ImGui::TableSetupColumn("Type");
10630
0
                ImGui::TableHeadersRow();
10631
0
                if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())
10632
0
                    if (sort_specs->SpecsDirty || RequestSort)
10633
0
                    {
10634
0
                        ExampleAsset::SortWithSortSpecs(sort_specs, Items.Data, Items.Size);
10635
0
                        sort_specs->SpecsDirty = RequestSort = false;
10636
0
                    }
10637
0
                ImGui::EndTable();
10638
0
            }
10639
0
            ImGui::PopStyleVar();
10640
0
        }
10641
10642
0
        ImGuiIO& io = ImGui::GetIO();
10643
0
        ImGui::SetNextWindowContentSize(ImVec2(0.0f, LayoutOuterPadding + LayoutLineCount * (LayoutItemSize.y + LayoutItemSpacing)));
10644
0
        if (ImGui::BeginChild("Assets", ImVec2(0.0f, -ImGui::GetTextLineHeightWithSpacing()), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove))
10645
0
        {
10646
0
            ImDrawList* draw_list = ImGui::GetWindowDrawList();
10647
10648
0
            const float avail_width = ImGui::GetContentRegionAvail().x;
10649
0
            UpdateLayoutSizes(avail_width);
10650
10651
            // Calculate and store start position.
10652
0
            ImVec2 start_pos = ImGui::GetCursorScreenPos();
10653
0
            start_pos = ImVec2(start_pos.x + LayoutOuterPadding, start_pos.y + LayoutOuterPadding);
10654
0
            ImGui::SetCursorScreenPos(start_pos);
10655
10656
            // Multi-select
10657
0
            ImGuiMultiSelectFlags ms_flags = ImGuiMultiSelectFlags_ClearOnEscape | ImGuiMultiSelectFlags_ClearOnClickVoid;
10658
10659
            // - Enable box-select (in 2D mode, so that changing box-select rectangle X1/X2 boundaries will affect clipped items)
10660
0
            if (AllowBoxSelect)
10661
0
                ms_flags |= ImGuiMultiSelectFlags_BoxSelect2d;
10662
10663
            // - This feature allows dragging an unselected item without selecting it (rarely used)
10664
0
            if (AllowDragUnselected)
10665
0
                ms_flags |= ImGuiMultiSelectFlags_SelectOnClickRelease;
10666
10667
            // - Enable keyboard wrapping on X axis
10668
            // (FIXME-MULTISELECT: We haven't designed/exposed a general nav wrapping api yet, so this flag is provided as a courtesy to avoid doing:
10669
            //    ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
10670
            // When we finish implementing a more general API for this, we will obsolete this flag in favor of the new system)
10671
0
            ms_flags |= ImGuiMultiSelectFlags_NavWrapX;
10672
10673
0
            ImGuiMultiSelectIO* ms_io = ImGui::BeginMultiSelect(ms_flags, Selection.Size, Items.Size);
10674
10675
            // Use custom selection adapter: store ID in selection (recommended)
10676
0
            Selection.UserData = this;
10677
0
            Selection.AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage* self_, int idx) { ExampleAssetsBrowser* self = (ExampleAssetsBrowser*)self_->UserData; return self->Items[idx].ID; };
10678
0
            Selection.ApplyRequests(ms_io);
10679
10680
0
            const bool want_delete = (ImGui::Shortcut(ImGuiKey_Delete, ImGuiInputFlags_Repeat) && (Selection.Size > 0)) || RequestDelete;
10681
0
            const int item_curr_idx_to_focus = want_delete ? Selection.ApplyDeletionPreLoop(ms_io, Items.Size) : -1;
10682
0
            RequestDelete = false;
10683
10684
            // Push LayoutSelectableSpacing (which is LayoutItemSpacing minus hit-spacing, if we decide to have hit gaps between items)
10685
            // Altering style ItemSpacing may seem unnecessary as we position every items using SetCursorScreenPos()...
10686
            // But it is necessary for two reasons:
10687
            // - Selectables uses it by default to visually fill the space between two items.
10688
            // - The vertical spacing would be measured by Clipper to calculate line height if we didn't provide it explicitly (here we do).
10689
0
            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(LayoutSelectableSpacing, LayoutSelectableSpacing));
10690
10691
            // Rendering parameters
10692
0
            const ImU32 icon_type_overlay_colors[3] = { 0, IM_COL32(200, 70, 70, 255), IM_COL32(70, 170, 70, 255) };
10693
0
            const ImU32 icon_bg_color = ImGui::GetColorU32(IM_COL32(35, 35, 35, 220));
10694
0
            const ImVec2 icon_type_overlay_size = ImVec2(4.0f, 4.0f);
10695
0
            const bool display_label = (LayoutItemSize.x >= ImGui::CalcTextSize("999").x);
10696
10697
0
            const int column_count = LayoutColumnCount;
10698
0
            ImGuiListClipper clipper;
10699
0
            clipper.Begin(LayoutLineCount, LayoutItemStep.y);
10700
0
            if (item_curr_idx_to_focus != -1)
10701
0
                clipper.IncludeItemByIndex(item_curr_idx_to_focus / column_count); // Ensure focused item line is not clipped.
10702
0
            if (ms_io->RangeSrcItem != -1)
10703
0
                clipper.IncludeItemByIndex((int)ms_io->RangeSrcItem / column_count); // Ensure RangeSrc item line is not clipped.
10704
0
            while (clipper.Step())
10705
0
            {
10706
0
                for (int line_idx = clipper.DisplayStart; line_idx < clipper.DisplayEnd; line_idx++)
10707
0
                {
10708
0
                    const int item_min_idx_for_current_line = line_idx * column_count;
10709
0
                    const int item_max_idx_for_current_line = IM_MIN((line_idx + 1) * column_count, Items.Size);
10710
0
                    for (int item_idx = item_min_idx_for_current_line; item_idx < item_max_idx_for_current_line; ++item_idx)
10711
0
                    {
10712
0
                        ExampleAsset* item_data = &Items[item_idx];
10713
0
                        ImGui::PushID((int)item_data->ID);
10714
10715
                        // Position item
10716
0
                        ImVec2 pos = ImVec2(start_pos.x + (item_idx % column_count) * LayoutItemStep.x, start_pos.y + line_idx * LayoutItemStep.y);
10717
0
                        ImGui::SetCursorScreenPos(pos);
10718
10719
0
                        ImGui::SetNextItemSelectionUserData(item_idx);
10720
0
                        bool item_is_selected = Selection.Contains((ImGuiID)item_data->ID);
10721
0
                        bool item_is_visible = ImGui::IsRectVisible(LayoutItemSize);
10722
0
                        ImGui::Selectable("", item_is_selected, ImGuiSelectableFlags_None, LayoutItemSize);
10723
10724
                        // Update our selection state immediately (without waiting for EndMultiSelect() requests)
10725
                        // because we use this to alter the color of our text/icon.
10726
0
                        if (ImGui::IsItemToggledSelection())
10727
0
                            item_is_selected = !item_is_selected;
10728
10729
                        // Focus (for after deletion)
10730
0
                        if (item_curr_idx_to_focus == item_idx)
10731
0
                            ImGui::SetKeyboardFocusHere(-1);
10732
10733
                        // Drag and drop
10734
0
                        if (ImGui::BeginDragDropSource())
10735
0
                        {
10736
                            // Create payload with full selection OR single unselected item.
10737
                            // (the later is only possible when using ImGuiMultiSelectFlags_SelectOnClickRelease)
10738
0
                            if (ImGui::GetDragDropPayload() == NULL)
10739
0
                            {
10740
0
                                ImVector<ImGuiID> payload_items;
10741
0
                                void* it = NULL;
10742
0
                                ImGuiID id = 0;
10743
0
                                if (!item_is_selected)
10744
0
                                    payload_items.push_back(item_data->ID);
10745
0
                                else
10746
0
                                    while (Selection.GetNextSelectedItem(&it, &id))
10747
0
                                        payload_items.push_back(id);
10748
0
                                ImGui::SetDragDropPayload("ASSETS_BROWSER_ITEMS", payload_items.Data, (size_t)payload_items.size_in_bytes());
10749
0
                            }
10750
10751
                            // Display payload content in tooltip, by extracting it from the payload data
10752
                            // (we could read from selection, but it is more correct and reusable to read from payload)
10753
0
                            const ImGuiPayload* payload = ImGui::GetDragDropPayload();
10754
0
                            const int payload_count = (int)payload->DataSize / (int)sizeof(ImGuiID);
10755
0
                            ImGui::Text("%d assets", payload_count);
10756
10757
0
                            ImGui::EndDragDropSource();
10758
0
                        }
10759
10760
                        // Render icon (a real app would likely display an image/thumbnail here)
10761
                        // Because we use ImGuiMultiSelectFlags_BoxSelect2d, clipping vertical may occasionally be larger, so we coarse-clip our rendering as well.
10762
0
                        if (item_is_visible)
10763
0
                        {
10764
0
                            ImVec2 box_min(pos.x - 1, pos.y - 1);
10765
0
                            ImVec2 box_max(box_min.x + LayoutItemSize.x + 2, box_min.y + LayoutItemSize.y + 2); // Dubious
10766
0
                            draw_list->AddRectFilled(box_min, box_max, icon_bg_color); // Background color
10767
0
                            if (ShowTypeOverlay && item_data->Type != 0)
10768
0
                            {
10769
0
                                ImU32 type_col = icon_type_overlay_colors[item_data->Type % IM_ARRAYSIZE(icon_type_overlay_colors)];
10770
0
                                draw_list->AddRectFilled(ImVec2(box_max.x - 2 - icon_type_overlay_size.x, box_min.y + 2), ImVec2(box_max.x - 2, box_min.y + 2 + icon_type_overlay_size.y), type_col);
10771
0
                            }
10772
0
                            if (display_label)
10773
0
                            {
10774
0
                                ImU32 label_col = ImGui::GetColorU32(item_is_selected ? ImGuiCol_Text : ImGuiCol_TextDisabled);
10775
0
                                char label[32];
10776
0
                                sprintf(label, "%d", item_data->ID);
10777
0
                                draw_list->AddText(ImVec2(box_min.x, box_max.y - ImGui::GetFontSize()), label_col, label);
10778
0
                            }
10779
0
                        }
10780
10781
0
                        ImGui::PopID();
10782
0
                    }
10783
0
                }
10784
0
            }
10785
0
            clipper.End();
10786
0
            ImGui::PopStyleVar(); // ImGuiStyleVar_ItemSpacing
10787
10788
            // Context menu
10789
0
            if (ImGui::BeginPopupContextWindow())
10790
0
            {
10791
0
                ImGui::Text("Selection: %d items", Selection.Size);
10792
0
                ImGui::Separator();
10793
0
                if (ImGui::MenuItem("Delete", "Del", false, Selection.Size > 0))
10794
0
                    RequestDelete = true;
10795
0
                ImGui::EndPopup();
10796
0
            }
10797
10798
0
            ms_io = ImGui::EndMultiSelect();
10799
0
            Selection.ApplyRequests(ms_io);
10800
0
            if (want_delete)
10801
0
                Selection.ApplyDeletionPostLoop(ms_io, Items, item_curr_idx_to_focus);
10802
10803
            // Zooming with CTRL+Wheel
10804
0
            if (ImGui::IsWindowAppearing())
10805
0
                ZoomWheelAccum = 0.0f;
10806
0
            if (ImGui::IsWindowHovered() && io.MouseWheel != 0.0f && ImGui::IsKeyDown(ImGuiMod_Ctrl) && ImGui::IsAnyItemActive() == false)
10807
0
            {
10808
0
                ZoomWheelAccum += io.MouseWheel;
10809
0
                if (fabsf(ZoomWheelAccum) >= 1.0f)
10810
0
                {
10811
                    // Calculate hovered item index from mouse location
10812
                    // FIXME: Locking aiming on 'hovered_item_idx' (with a cool-down timer) would ensure zoom keeps on it.
10813
0
                    const float hovered_item_nx = (io.MousePos.x - start_pos.x + LayoutItemSpacing * 0.5f) / LayoutItemStep.x;
10814
0
                    const float hovered_item_ny = (io.MousePos.y - start_pos.y + LayoutItemSpacing * 0.5f) / LayoutItemStep.y;
10815
0
                    const int hovered_item_idx = ((int)hovered_item_ny * LayoutColumnCount) + (int)hovered_item_nx;
10816
                    //ImGui::SetTooltip("%f,%f -> item %d", hovered_item_nx, hovered_item_ny, hovered_item_idx); // Move those 4 lines in block above for easy debugging
10817
10818
                    // Zoom
10819
0
                    IconSize *= powf(1.1f, (float)(int)ZoomWheelAccum);
10820
0
                    IconSize = IM_CLAMP(IconSize, 16.0f, 128.0f);
10821
0
                    ZoomWheelAccum -= (int)ZoomWheelAccum;
10822
0
                    UpdateLayoutSizes(avail_width);
10823
10824
                    // Manipulate scroll to that we will land at the same Y location of currently hovered item.
10825
                    // - Calculate next frame position of item under mouse
10826
                    // - Set new scroll position to be used in next ImGui::BeginChild() call.
10827
0
                    float hovered_item_rel_pos_y = ((float)(hovered_item_idx / LayoutColumnCount) + fmodf(hovered_item_ny, 1.0f)) * LayoutItemStep.y;
10828
0
                    hovered_item_rel_pos_y += ImGui::GetStyle().WindowPadding.y;
10829
0
                    float mouse_local_y = io.MousePos.y - ImGui::GetWindowPos().y;
10830
0
                    ImGui::SetScrollY(hovered_item_rel_pos_y - mouse_local_y);
10831
0
                }
10832
0
            }
10833
0
        }
10834
0
        ImGui::EndChild();
10835
10836
0
        ImGui::Text("Selected: %d/%d items", Selection.Size, Items.Size);
10837
0
        ImGui::End();
10838
0
    }
10839
};
10840
10841
void ShowExampleAppAssetsBrowser(bool* p_open)
10842
0
{
10843
    IMGUI_DEMO_MARKER("Examples/Assets Browser");
10844
0
    static ExampleAssetsBrowser assets_browser;
10845
0
    assets_browser.Draw("Example: Assets Browser", p_open);
10846
0
}
10847
10848
// End of Demo code
10849
#else
10850
10851
void ImGui::ShowAboutWindow(bool*) {}
10852
void ImGui::ShowDemoWindow(bool*) {}
10853
void ImGui::ShowUserGuide() {}
10854
void ImGui::ShowStyleEditor(ImGuiStyle*) {}
10855
bool ImGui::ShowStyleSelector(const char*) { return false; }
10856
10857
#endif // #ifndef IMGUI_DISABLE_DEMO_WINDOWS
10858
10859
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui_draw.cpp
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (drawing and font code)
3
4
/*
5
6
Index of this file:
7
8
// [SECTION] STB libraries implementation
9
// [SECTION] Style functions
10
// [SECTION] ImDrawList
11
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
12
// [SECTION] ImDrawListSplitter
13
// [SECTION] ImDrawData
14
// [SECTION] Helpers ShadeVertsXXX functions
15
// [SECTION] ImFontConfig
16
// [SECTION] ImFontAtlas, ImFontAtlasBuilder
17
// [SECTION] ImFontAtlas: backend for stb_truetype
18
// [SECTION] ImFontAtlas: glyph ranges helpers
19
// [SECTION] ImFontGlyphRangesBuilder
20
// [SECTION] ImFont
21
// [SECTION] ImGui Internal Render Helpers
22
// [SECTION] Decompression code
23
// [SECTION] Default font data (ProggyClean.ttf)
24
25
*/
26
27
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
28
#define _CRT_SECURE_NO_WARNINGS
29
#endif
30
31
#ifndef IMGUI_DEFINE_MATH_OPERATORS
32
#define IMGUI_DEFINE_MATH_OPERATORS
33
#endif
34
35
#include "imgui.h"
36
#ifndef IMGUI_DISABLE
37
#include "imgui_internal.h"
38
#ifdef IMGUI_ENABLE_FREETYPE
39
#include "misc/freetype/imgui_freetype.h"
40
#endif
41
42
#include <stdio.h>      // vsnprintf, sscanf, printf
43
#include <stdint.h>     // intptr_t
44
45
// Visual Studio warnings
46
#ifdef _MSC_VER
47
#pragma warning (disable: 4127)     // condition expression is constant
48
#pragma warning (disable: 4505)     // unreferenced local function has been removed (stb stuff)
49
#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
50
#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
51
#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
52
#endif
53
54
// Clang/GCC warnings with -Weverything
55
#if defined(__clang__)
56
#if __has_warning("-Wunknown-warning-option")
57
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
58
#endif
59
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
60
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
61
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
62
#pragma clang diagnostic ignored "-Wglobal-constructors"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.
63
#pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
64
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
65
#pragma clang diagnostic ignored "-Wcomma"                          // warning: possible misuse of comma operator here
66
#pragma clang diagnostic ignored "-Wreserved-id-macro"              // warning: macro name is a reserved identifier
67
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
68
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
69
#pragma clang diagnostic ignored "-Wreserved-identifier"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
70
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
71
#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
72
#pragma clang diagnostic ignored "-Wcast-qual"                      // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier
73
#pragma clang diagnostic ignored "-Wswitch-default"                 // warning: 'switch' missing 'default' label
74
#elif defined(__GNUC__)
75
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
76
#pragma GCC diagnostic ignored "-Wunused-function"                  // warning: 'xxxx' defined but not used
77
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
78
#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
79
#pragma GCC diagnostic ignored "-Wconversion"                       // warning: conversion to 'xxxx' from 'xxxx' may alter its value
80
#pragma GCC diagnostic ignored "-Wstack-protector"                  // warning: stack protector not protecting local variables: variable length buffer
81
#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
82
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
83
#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
84
#endif
85
86
//-------------------------------------------------------------------------
87
// [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack)
88
//-------------------------------------------------------------------------
89
90
// Compile time options:
91
//#define IMGUI_STB_NAMESPACE           ImStb
92
//#define IMGUI_STB_TRUETYPE_FILENAME   "my_folder/stb_truetype.h"
93
//#define IMGUI_STB_RECT_PACK_FILENAME  "my_folder/stb_rect_pack.h"
94
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
95
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
96
97
#ifdef IMGUI_STB_NAMESPACE
98
namespace IMGUI_STB_NAMESPACE
99
{
100
#endif
101
102
#ifdef _MSC_VER
103
#pragma warning (push)
104
#pragma warning (disable: 4456)                             // declaration of 'xx' hides previous local declaration
105
#pragma warning (disable: 6011)                             // (stb_rectpack) Dereferencing NULL pointer 'cur->next'.
106
#pragma warning (disable: 6385)                             // (stb_truetype) Reading invalid data from 'buffer':  the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read.
107
#pragma warning (disable: 28182)                            // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did.
108
#endif
109
110
#if defined(__clang__)
111
#pragma clang diagnostic push
112
#pragma clang diagnostic ignored "-Wunused-function"        // warning: 'xxxx' defined but not used
113
#pragma clang diagnostic ignored "-Wmissing-prototypes"
114
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
115
#endif
116
117
#if defined(__GNUC__)
118
#pragma GCC diagnostic push
119
#pragma GCC diagnostic ignored "-Wtype-limits"              // warning: comparison is always true due to limited range of data type [-Wtype-limits]
120
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"     // warning: this statement may fall through
121
#endif
122
123
#ifndef STB_RECT_PACK_IMPLEMENTATION                        // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
124
#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION          // in case the user already have an implementation in another compilation unit
125
#define STBRP_STATIC
126
781
#define STBRP_ASSERT(x)     do { IM_ASSERT(x); } while (0)
127
44
#define STBRP_SORT          ImQsort
128
#define STB_RECT_PACK_IMPLEMENTATION
129
#endif
130
#ifdef IMGUI_STB_RECT_PACK_FILENAME
131
#include IMGUI_STB_RECT_PACK_FILENAME
132
#else
133
#include "imstb_rectpack.h"
134
#endif
135
#endif
136
137
#ifdef  IMGUI_ENABLE_STB_TRUETYPE
138
#ifndef STB_TRUETYPE_IMPLEMENTATION                         // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)
139
#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION           // in case the user already have an implementation in another compilation unit
140
100
#define STBTT_malloc(x,u)   ((void)(u), IM_ALLOC(x))
141
100
#define STBTT_free(x,u)     ((void)(u), IM_FREE(x))
142
8.97k
#define STBTT_assert(x)     do { IM_ASSERT(x); } while(0)
143
#define STBTT_fmod(x,y)     ImFmod(x,y)
144
0
#define STBTT_sqrt(x)       ImSqrt(x)
145
#define STBTT_pow(x,y)      ImPow(x,y)
146
763
#define STBTT_fabs(x)       ImFabs(x)
147
120
#define STBTT_ifloor(x)     ((int)ImFloor(x))
148
80
#define STBTT_iceil(x)      ((int)ImCeil(x))
149
#define STBTT_strlen(x)     ImStrlen(x)
150
#define STBTT_STATIC
151
#define STB_TRUETYPE_IMPLEMENTATION
152
#else
153
#define STBTT_DEF extern
154
#endif
155
#ifdef IMGUI_STB_TRUETYPE_FILENAME
156
#include IMGUI_STB_TRUETYPE_FILENAME
157
#else
158
#include "imstb_truetype.h"
159
#endif
160
#endif
161
#endif // IMGUI_ENABLE_STB_TRUETYPE
162
163
#if defined(__GNUC__)
164
#pragma GCC diagnostic pop
165
#endif
166
167
#if defined(__clang__)
168
#pragma clang diagnostic pop
169
#endif
170
171
#if defined(_MSC_VER)
172
#pragma warning (pop)
173
#endif
174
175
#ifdef IMGUI_STB_NAMESPACE
176
} // namespace ImStb
177
using namespace IMGUI_STB_NAMESPACE;
178
#endif
179
180
//-----------------------------------------------------------------------------
181
// [SECTION] Style functions
182
//-----------------------------------------------------------------------------
183
184
void ImGui::StyleColorsDark(ImGuiStyle* dst)
185
2
{
186
2
    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
187
2
    ImVec4* colors = style->Colors;
188
189
2
    colors[ImGuiCol_Text]                   = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
190
2
    colors[ImGuiCol_TextDisabled]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
191
2
    colors[ImGuiCol_WindowBg]               = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);
192
2
    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
193
2
    colors[ImGuiCol_PopupBg]                = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
194
2
    colors[ImGuiCol_Border]                 = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
195
2
    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
196
2
    colors[ImGuiCol_FrameBg]                = ImVec4(0.16f, 0.29f, 0.48f, 0.54f);
197
2
    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
198
2
    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
199
2
    colors[ImGuiCol_TitleBg]                = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);
200
2
    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.16f, 0.29f, 0.48f, 1.00f);
201
2
    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
202
2
    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
203
2
    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
204
2
    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
205
2
    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
206
2
    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
207
2
    colors[ImGuiCol_CheckMark]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
208
2
    colors[ImGuiCol_SliderGrab]             = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);
209
2
    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
210
2
    colors[ImGuiCol_Button]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
211
2
    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
212
2
    colors[ImGuiCol_ButtonActive]           = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
213
2
    colors[ImGuiCol_Header]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
214
2
    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
215
2
    colors[ImGuiCol_HeaderActive]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
216
2
    colors[ImGuiCol_Separator]              = colors[ImGuiCol_Border];
217
2
    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);
218
2
    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);
219
2
    colors[ImGuiCol_ResizeGrip]             = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);
220
2
    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
221
2
    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
222
2
    colors[ImGuiCol_InputTextCursor]        = colors[ImGuiCol_Text];
223
2
    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];
224
2
    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f);
225
2
    colors[ImGuiCol_TabSelected]            = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
226
2
    colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];
227
2
    colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);
228
2
    colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);
229
2
    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f);
230
2
    colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
231
2
    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
232
2
    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
233
2
    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
234
2
    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);
235
2
    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);   // Prefer using Alpha=1.0 here
236
2
    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);   // Prefer using Alpha=1.0 here
237
2
    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
238
2
    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
239
2
    colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];
240
2
    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
241
2
    colors[ImGuiCol_TreeLines]              = colors[ImGuiCol_Border];
242
2
    colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
243
2
    colors[ImGuiCol_UnsavedMarker]          = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
244
2
    colors[ImGuiCol_NavCursor]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
245
2
    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
246
2
    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
247
2
    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
248
2
}
249
250
void ImGui::StyleColorsClassic(ImGuiStyle* dst)
251
0
{
252
0
    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
253
0
    ImVec4* colors = style->Colors;
254
255
0
    colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
256
0
    colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
257
0
    colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 0.85f);
258
0
    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
259
0
    colors[ImGuiCol_PopupBg]                = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);
260
0
    colors[ImGuiCol_Border]                 = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);
261
0
    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
262
0
    colors[ImGuiCol_FrameBg]                = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);
263
0
    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.47f, 0.47f, 0.69f, 0.40f);
264
0
    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.42f, 0.41f, 0.64f, 0.69f);
265
0
    colors[ImGuiCol_TitleBg]                = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);
266
0
    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);
267
0
    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);
268
0
    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);
269
0
    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
270
0
    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);
271
0
    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);
272
0
    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);
273
0
    colors[ImGuiCol_CheckMark]              = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);
274
0
    colors[ImGuiCol_SliderGrab]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);
275
0
    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);
276
0
    colors[ImGuiCol_Button]                 = ImVec4(0.35f, 0.40f, 0.61f, 0.62f);
277
0
    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.40f, 0.48f, 0.71f, 0.79f);
278
0
    colors[ImGuiCol_ButtonActive]           = ImVec4(0.46f, 0.54f, 0.80f, 1.00f);
279
0
    colors[ImGuiCol_Header]                 = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);
280
0
    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);
281
0
    colors[ImGuiCol_HeaderActive]           = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);
282
0
    colors[ImGuiCol_Separator]              = ImVec4(0.50f, 0.50f, 0.50f, 0.60f);
283
0
    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);
284
0
    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);
285
0
    colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
286
0
    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
287
0
    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
288
0
    colors[ImGuiCol_InputTextCursor]        = colors[ImGuiCol_Text];
289
0
    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];
290
0
    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f);
291
0
    colors[ImGuiCol_TabSelected]            = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
292
0
    colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];
293
0
    colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);
294
0
    colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);
295
0
    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.53f, 0.53f, 0.87f, 0.00f);
296
0
    colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
297
0
    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
298
0
    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
299
0
    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
300
0
    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.27f, 0.27f, 0.38f, 1.00f);
301
0
    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.45f, 1.00f);   // Prefer using Alpha=1.0 here
302
0
    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.26f, 0.26f, 0.28f, 1.00f);   // Prefer using Alpha=1.0 here
303
0
    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
304
0
    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
305
0
    colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];
306
0
    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
307
0
    colors[ImGuiCol_TreeLines]              = colors[ImGuiCol_Border];
308
0
    colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
309
0
    colors[ImGuiCol_UnsavedMarker]          = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);
310
0
    colors[ImGuiCol_NavCursor]              = colors[ImGuiCol_HeaderHovered];
311
0
    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
312
0
    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
313
0
    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
314
0
}
315
316
// Those light colors are better suited with a thicker font than the default one + FrameBorder
317
void ImGui::StyleColorsLight(ImGuiStyle* dst)
318
0
{
319
0
    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
320
0
    ImVec4* colors = style->Colors;
321
322
0
    colors[ImGuiCol_Text]                   = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
323
0
    colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
324
0
    colors[ImGuiCol_WindowBg]               = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);
325
0
    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
326
0
    colors[ImGuiCol_PopupBg]                = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);
327
0
    colors[ImGuiCol_Border]                 = ImVec4(0.00f, 0.00f, 0.00f, 0.30f);
328
0
    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
329
0
    colors[ImGuiCol_FrameBg]                = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
330
0
    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
331
0
    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
332
0
    colors[ImGuiCol_TitleBg]                = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);
333
0
    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);
334
0
    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);
335
0
    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);
336
0
    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);
337
0
    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.69f, 0.69f, 0.69f, 0.80f);
338
0
    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.49f, 0.49f, 0.49f, 0.80f);
339
0
    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);
340
0
    colors[ImGuiCol_CheckMark]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
341
0
    colors[ImGuiCol_SliderGrab]             = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);
342
0
    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.46f, 0.54f, 0.80f, 0.60f);
343
0
    colors[ImGuiCol_Button]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
344
0
    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
345
0
    colors[ImGuiCol_ButtonActive]           = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);
346
0
    colors[ImGuiCol_Header]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);
347
0
    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
348
0
    colors[ImGuiCol_HeaderActive]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
349
0
    colors[ImGuiCol_Separator]              = ImVec4(0.39f, 0.39f, 0.39f, 0.62f);
350
0
    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.14f, 0.44f, 0.80f, 0.78f);
351
0
    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.14f, 0.44f, 0.80f, 1.00f);
352
0
    colors[ImGuiCol_ResizeGrip]             = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);
353
0
    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
354
0
    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
355
0
    colors[ImGuiCol_InputTextCursor]        = colors[ImGuiCol_Text];
356
0
    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];
357
0
    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.90f);
358
0
    colors[ImGuiCol_TabSelected]            = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
359
0
    colors[ImGuiCol_TabSelectedOverline]    = colors[ImGuiCol_HeaderActive];
360
0
    colors[ImGuiCol_TabDimmed]              = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);
361
0
    colors[ImGuiCol_TabDimmedSelected]      = ImLerp(colors[ImGuiCol_TabSelected],  colors[ImGuiCol_TitleBg], 0.40f);
362
0
    colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 0.00f);
363
0
    colors[ImGuiCol_PlotLines]              = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
364
0
    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
365
0
    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
366
0
    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.45f, 0.00f, 1.00f);
367
0
    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.78f, 0.87f, 0.98f, 1.00f);
368
0
    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.57f, 0.57f, 0.64f, 1.00f);   // Prefer using Alpha=1.0 here
369
0
    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.68f, 0.68f, 0.74f, 1.00f);   // Prefer using Alpha=1.0 here
370
0
    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
371
0
    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);
372
0
    colors[ImGuiCol_TextLink]               = colors[ImGuiCol_HeaderActive];
373
0
    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
374
0
    colors[ImGuiCol_TreeLines]              = colors[ImGuiCol_Border];
375
0
    colors[ImGuiCol_DragDropTarget]         = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
376
0
    colors[ImGuiCol_UnsavedMarker]          = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);
377
0
    colors[ImGuiCol_NavCursor]              = colors[ImGuiCol_HeaderHovered];
378
0
    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);
379
0
    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);
380
0
    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
381
0
}
382
383
//-----------------------------------------------------------------------------
384
// [SECTION] ImDrawList
385
//-----------------------------------------------------------------------------
386
387
ImDrawListSharedData::ImDrawListSharedData()
388
1
{
389
1
    memset(this, 0, sizeof(*this));
390
1
    InitialFringeScale = 1.0f;
391
49
    for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++)
392
48
    {
393
48
        const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);
394
48
        ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));
395
48
    }
396
1
    ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
397
1
}
398
399
ImDrawListSharedData::~ImDrawListSharedData()
400
1
{
401
1
    IM_ASSERT(DrawLists.Size == 0);
402
1
}
403
404
void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
405
80.5k
{
406
80.5k
    if (CircleSegmentMaxError == max_error)
407
80.5k
        return;
408
409
1
    IM_ASSERT(max_error > 0.0f);
410
1
    CircleSegmentMaxError = max_error;
411
65
    for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++)
412
64
    {
413
64
        const float radius = (float)i;
414
64
        CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX);
415
64
    }
416
1
    ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);
417
1
}
418
419
ImDrawList::ImDrawList(ImDrawListSharedData* shared_data)
420
3
{
421
3
    memset(this, 0, sizeof(*this));
422
3
    _SetDrawListSharedData(shared_data);
423
3
}
424
425
ImDrawList::~ImDrawList()
426
3
{
427
3
    _ClearFreeMemory();
428
3
    _SetDrawListSharedData(NULL);
429
3
}
430
431
void ImDrawList::_SetDrawListSharedData(ImDrawListSharedData* data)
432
9
{
433
9
    if (_Data != NULL)
434
3
        _Data->DrawLists.find_erase_unsorted(this);
435
9
    _Data = data;
436
9
    if (_Data != NULL)
437
3
        _Data->DrawLists.push_back(this);
438
9
}
439
440
// Initialize before use in a new frame. We always have a command ready in the buffer.
441
// In the majority of cases, you would want to call PushClipRect() and PushTexture() after this.
442
void ImDrawList::_ResetForNewFrame()
443
161k
{
444
    // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
445
161k
    IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);
446
161k
    IM_STATIC_ASSERT(offsetof(ImDrawCmd, TexRef) == sizeof(ImVec4));
447
161k
    IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureRef));
448
161k
    if (_Splitter._Count > 1)
449
0
        _Splitter.Merge(this);
450
451
161k
    CmdBuffer.resize(0);
452
161k
    IdxBuffer.resize(0);
453
161k
    VtxBuffer.resize(0);
454
161k
    Flags = _Data->InitialFlags;
455
161k
    memset(&_CmdHeader, 0, sizeof(_CmdHeader));
456
161k
    _VtxCurrentIdx = 0;
457
161k
    _VtxWritePtr = NULL;
458
161k
    _IdxWritePtr = NULL;
459
161k
    _ClipRectStack.resize(0);
460
161k
    _TextureStack.resize(0);
461
161k
    _CallbacksDataBuf.resize(0);
462
161k
    _Path.resize(0);
463
161k
    _Splitter.Clear();
464
161k
    CmdBuffer.push_back(ImDrawCmd());
465
161k
    _FringeScale = _Data->InitialFringeScale;
466
161k
}
467
468
void ImDrawList::_ClearFreeMemory()
469
4
{
470
4
    CmdBuffer.clear();
471
4
    IdxBuffer.clear();
472
4
    VtxBuffer.clear();
473
4
    Flags = ImDrawListFlags_None;
474
4
    _VtxCurrentIdx = 0;
475
4
    _VtxWritePtr = NULL;
476
4
    _IdxWritePtr = NULL;
477
4
    _ClipRectStack.clear();
478
4
    _TextureStack.clear();
479
4
    _CallbacksDataBuf.clear();
480
4
    _Path.clear();
481
4
    _Splitter.ClearFreeMemory();
482
4
}
483
484
// Note: For multi-threaded rendering, consider using `imgui_threaded_rendering` from https://github.com/ocornut/imgui_club
485
ImDrawList* ImDrawList::CloneOutput() const
486
0
{
487
0
    ImDrawList* dst = IM_NEW(ImDrawList(NULL));
488
0
    dst->CmdBuffer = CmdBuffer;
489
0
    dst->IdxBuffer = IdxBuffer;
490
0
    dst->VtxBuffer = VtxBuffer;
491
0
    dst->Flags = Flags;
492
0
    return dst;
493
0
}
494
495
void ImDrawList::AddDrawCmd()
496
243k
{
497
243k
    ImDrawCmd draw_cmd;
498
243k
    draw_cmd.ClipRect = _CmdHeader.ClipRect;    // Same as calling ImDrawCmd_HeaderCopy()
499
243k
    draw_cmd.TexRef = _CmdHeader.TexRef;
500
243k
    draw_cmd.VtxOffset = _CmdHeader.VtxOffset;
501
243k
    draw_cmd.IdxOffset = IdxBuffer.Size;
502
503
243k
    IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);
504
243k
    CmdBuffer.push_back(draw_cmd);
505
243k
}
506
507
// Pop trailing draw command (used before merging or presenting to user)
508
// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL
509
void ImDrawList::_PopUnusedDrawCmd()
510
81.2k
{
511
162k
    while (CmdBuffer.Size > 0)
512
162k
    {
513
162k
        ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
514
162k
        if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL)
515
81.2k
            return;// break;
516
81.2k
        CmdBuffer.pop_back();
517
81.2k
    }
518
81.2k
}
519
520
void ImDrawList::AddCallback(ImDrawCallback callback, void* userdata, size_t userdata_size)
521
0
{
522
0
    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
523
0
    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
524
0
    IM_ASSERT(callback != NULL);
525
0
    IM_ASSERT(curr_cmd->UserCallback == NULL);
526
0
    if (curr_cmd->ElemCount != 0)
527
0
    {
528
0
        AddDrawCmd();
529
0
        curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
530
0
    }
531
532
0
    curr_cmd->UserCallback = callback;
533
0
    if (userdata_size == 0)
534
0
    {
535
        // Store user data directly in command (no indirection)
536
0
        curr_cmd->UserCallbackData = userdata;
537
0
        curr_cmd->UserCallbackDataSize = 0;
538
0
        curr_cmd->UserCallbackDataOffset = -1;
539
0
    }
540
0
    else
541
0
    {
542
        // Copy and store user data in a buffer
543
0
        IM_ASSERT(userdata != NULL);
544
0
        IM_ASSERT(userdata_size < (1u << 31));
545
0
        curr_cmd->UserCallbackData = NULL; // Will be resolved during Render()
546
0
        curr_cmd->UserCallbackDataSize = (int)userdata_size;
547
0
        curr_cmd->UserCallbackDataOffset = _CallbacksDataBuf.Size;
548
0
        _CallbacksDataBuf.resize(_CallbacksDataBuf.Size + (int)userdata_size);
549
0
        memcpy(_CallbacksDataBuf.Data + (size_t)curr_cmd->UserCallbackDataOffset, userdata, userdata_size);
550
0
    }
551
552
0
    AddDrawCmd(); // Force a new command after us (see comment below)
553
0
}
554
555
// Compare ClipRect, TexRef and VtxOffset with a single memcmp()
556
241k
#define ImDrawCmd_HeaderSize                            (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))
557
241k
#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)       (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize))    // Compare ClipRect, TexRef, VtxOffset
558
0
#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC)          (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize))    // Copy ClipRect, TexRef, VtxOffset
559
646k
#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1)  (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)
560
561
// Try to merge two last draw commands
562
void ImDrawList::_TryMergeDrawCmds()
563
0
{
564
0
    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
565
0
    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
566
0
    ImDrawCmd* prev_cmd = curr_cmd - 1;
567
0
    if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)
568
0
    {
569
0
        prev_cmd->ElemCount += curr_cmd->ElemCount;
570
0
        CmdBuffer.pop_back();
571
0
    }
572
0
}
573
574
// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.
575
// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.
576
void ImDrawList::_OnChangedClipRect()
577
646k
{
578
    // If current command is used with different settings we need to add a new command
579
646k
    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
580
646k
    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
581
646k
    if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0)
582
243k
    {
583
243k
        AddDrawCmd();
584
243k
        return;
585
243k
    }
586
403k
    IM_ASSERT(curr_cmd->UserCallback == NULL);
587
588
    // Try to merge with previous command if it matches, else use current command
589
403k
    ImDrawCmd* prev_cmd = curr_cmd - 1;
590
403k
    if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)
591
80.5k
    {
592
80.5k
        CmdBuffer.pop_back();
593
80.5k
        return;
594
80.5k
    }
595
323k
    curr_cmd->ClipRect = _CmdHeader.ClipRect;
596
323k
}
597
598
void ImDrawList::_OnChangedTexture()
599
161k
{
600
    // If current command is used with different settings we need to add a new command
601
161k
    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
602
161k
    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
603
161k
    if (curr_cmd->ElemCount != 0 && curr_cmd->TexRef != _CmdHeader.TexRef)
604
0
    {
605
0
        AddDrawCmd();
606
0
        return;
607
0
    }
608
609
    // Unlike other _OnChangedXXX functions this may be called by ImFontAtlasUpdateDrawListsTextures() in more locations so we need to handle this case.
610
161k
    if (curr_cmd->UserCallback != NULL)
611
0
        return;
612
613
    // Try to merge with previous command if it matches, else use current command
614
161k
    ImDrawCmd* prev_cmd = curr_cmd - 1;
615
161k
    if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)
616
0
    {
617
0
        CmdBuffer.pop_back();
618
0
        return;
619
0
    }
620
161k
    curr_cmd->TexRef = _CmdHeader.TexRef;
621
161k
}
622
623
void ImDrawList::_OnChangedVtxOffset()
624
0
{
625
    // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this.
626
0
    _VtxCurrentIdx = 0;
627
0
    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);
628
0
    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
629
    //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349
630
0
    if (curr_cmd->ElemCount != 0)
631
0
    {
632
0
        AddDrawCmd();
633
0
        return;
634
0
    }
635
0
    IM_ASSERT(curr_cmd->UserCallback == NULL);
636
0
    curr_cmd->VtxOffset = _CmdHeader.VtxOffset;
637
0
}
638
639
int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const
640
0
{
641
    // Automatic segment count
642
0
    const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy
643
0
    if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))
644
0
        return _Data->CircleSegmentCounts[radius_idx]; // Use cached value
645
0
    else
646
0
        return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);
647
0
}
648
649
// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
650
void ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect)
651
404k
{
652
404k
    ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);
653
404k
    if (intersect_with_current_clip_rect)
654
161k
    {
655
161k
        ImVec4 current = _CmdHeader.ClipRect;
656
161k
        if (cr.x < current.x) cr.x = current.x;
657
161k
        if (cr.y < current.y) cr.y = current.y;
658
161k
        if (cr.z > current.z) cr.z = current.z;
659
161k
        if (cr.w > current.w) cr.w = current.w;
660
161k
    }
661
404k
    cr.z = ImMax(cr.x, cr.z);
662
404k
    cr.w = ImMax(cr.y, cr.w);
663
664
404k
    _ClipRectStack.push_back(cr);
665
404k
    _CmdHeader.ClipRect = cr;
666
404k
    _OnChangedClipRect();
667
404k
}
668
669
void ImDrawList::PushClipRectFullScreen()
670
0
{
671
0
    PushClipRect(ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y), ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w));
672
0
}
673
674
void ImDrawList::PopClipRect()
675
242k
{
676
242k
    _ClipRectStack.pop_back();
677
242k
    _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1];
678
242k
    _OnChangedClipRect();
679
242k
}
680
681
void ImDrawList::PushTexture(ImTextureRef tex_ref)
682
161k
{
683
161k
    _TextureStack.push_back(tex_ref);
684
161k
    _CmdHeader.TexRef = tex_ref;
685
161k
    if (tex_ref._TexData != NULL)
686
161k
        IM_ASSERT(tex_ref._TexData->WantDestroyNextFrame == false);
687
161k
    _OnChangedTexture();
688
161k
}
689
690
void ImDrawList::PopTexture()
691
0
{
692
0
    _TextureStack.pop_back();
693
0
    _CmdHeader.TexRef = (_TextureStack.Size == 0) ? ImTextureRef() : _TextureStack.Data[_TextureStack.Size - 1];
694
0
    _OnChangedTexture();
695
0
}
696
697
// This is used by ImGui::PushFont()/PopFont(). It works because we never use _TextureIdStack[] elsewhere than in PushTexture()/PopTexture().
698
void ImDrawList::_SetTexture(ImTextureRef tex_ref)
699
0
{
700
0
    if (_CmdHeader.TexRef == tex_ref)
701
0
        return;
702
0
    _CmdHeader.TexRef = tex_ref;
703
0
    _TextureStack.back() = tex_ref;
704
0
    _OnChangedTexture();
705
0
}
706
707
// Reserve space for a number of vertices and indices.
708
// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or
709
// submit the intermediate results. PrimUnreserve() can be used to release unused allocations.
710
void ImDrawList::PrimReserve(int idx_count, int vtx_count)
711
892k
{
712
    // Large mesh support (when enabled)
713
892k
    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
714
892k
    if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset))
715
0
    {
716
        // FIXME: In theory we should be testing that vtx_count <64k here.
717
        // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us
718
        // to not make that check until we rework the text functions to handle clipping and large horizontal lines better.
719
0
        _CmdHeader.VtxOffset = VtxBuffer.Size;
720
0
        _OnChangedVtxOffset();
721
0
    }
722
723
892k
    ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
724
892k
    draw_cmd->ElemCount += idx_count;
725
726
892k
    int vtx_buffer_old_size = VtxBuffer.Size;
727
892k
    VtxBuffer.resize(vtx_buffer_old_size + vtx_count);
728
892k
    _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size;
729
730
892k
    int idx_buffer_old_size = IdxBuffer.Size;
731
892k
    IdxBuffer.resize(idx_buffer_old_size + idx_count);
732
892k
    _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;
733
892k
}
734
735
// Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve().
736
void ImDrawList::PrimUnreserve(int idx_count, int vtx_count)
737
0
{
738
0
    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);
739
740
0
    ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];
741
0
    draw_cmd->ElemCount -= idx_count;
742
0
    VtxBuffer.shrink(VtxBuffer.Size - vtx_count);
743
0
    IdxBuffer.shrink(IdxBuffer.Size - idx_count);
744
0
}
745
746
// Fully unrolled with inline call to keep our debug builds decently fast.
747
void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col)
748
324k
{
749
324k
    ImVec2 b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel);
750
324k
    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
751
324k
    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);
752
324k
    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);
753
324k
    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
754
324k
    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
755
324k
    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
756
324k
    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col;
757
324k
    _VtxWritePtr += 4;
758
324k
    _VtxCurrentIdx += 4;
759
324k
    _IdxWritePtr += 6;
760
324k
}
761
762
void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col)
763
0
{
764
0
    ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y);
765
0
    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
766
0
    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);
767
0
    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);
768
0
    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;
769
0
    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;
770
0
    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;
771
0
    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;
772
0
    _VtxWritePtr += 4;
773
0
    _VtxCurrentIdx += 4;
774
0
    _IdxWritePtr += 6;
775
0
}
776
777
void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col)
778
0
{
779
0
    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
780
0
    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);
781
0
    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);
782
0
    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;
783
0
    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;
784
0
    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;
785
0
    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;
786
0
    _VtxWritePtr += 4;
787
0
    _VtxCurrentIdx += 4;
788
0
    _IdxWritePtr += 6;
789
0
}
790
791
// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds.
792
// - Those macros expects l-values and need to be used as their own statement.
793
// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers.
794
890k
#define IM_NORMALIZE2F_OVER_ZERO(VX,VY)     { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0
795
890k
#define IM_FIXNORMAL2F_MAX_INVLEN2          100.0f // 500.0f (see #4053, #3366)
796
890k
#define IM_FIXNORMAL2F(VX,VY)               { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0
797
798
// TODO: Thickness anti-aliased lines cap are missing their AA fringe.
799
// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
800
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)
801
162k
{
802
162k
    if (points_count < 2 || (col & IM_COL32_A_MASK) == 0)
803
0
        return;
804
805
162k
    const bool closed = (flags & ImDrawFlags_Closed) != 0;
806
162k
    const ImVec2 opaque_uv = _Data->TexUvWhitePixel;
807
162k
    const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw
808
162k
    const bool thick_line = (thickness > _FringeScale);
809
810
162k
    if (Flags & ImDrawListFlags_AntiAliasedLines)
811
162k
    {
812
        // Anti-aliased stroke
813
162k
        const float AA_SIZE = _FringeScale;
814
162k
        const ImU32 col_trans = col & ~IM_COL32_A_MASK;
815
816
        // Thicknesses <1.0 should behave like thickness 1.0
817
162k
        thickness = ImMax(thickness, 1.0f);
818
162k
        const int integer_thickness = (int)thickness;
819
162k
        const float fractional_thickness = thickness - integer_thickness;
820
821
        // Do we want to draw this line using a texture?
822
        // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved.
823
        // - If AA_SIZE is not 1.0f we cannot use the texture path.
824
162k
        const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f);
825
826
        // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off
827
162k
        IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));
828
829
162k
        const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);
830
162k
        const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);
831
162k
        PrimReserve(idx_count, vtx_count);
832
833
        // Temporary buffer
834
        // The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point
835
162k
        _Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5));
836
162k
        ImVec2* temp_normals = _Data->TempBuffer.Data;
837
162k
        ImVec2* temp_points = temp_normals + points_count;
838
839
        // Calculate normals (tangents) for each line segment
840
811k
        for (int i1 = 0; i1 < count; i1++)
841
648k
        {
842
648k
            const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
843
648k
            float dx = points[i2].x - points[i1].x;
844
648k
            float dy = points[i2].y - points[i1].y;
845
648k
            IM_NORMALIZE2F_OVER_ZERO(dx, dy);
846
648k
            temp_normals[i1].x = dy;
847
648k
            temp_normals[i1].y = -dx;
848
648k
        }
849
162k
        if (!closed)
850
659
            temp_normals[points_count - 1] = temp_normals[points_count - 2];
851
852
        // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point
853
162k
        if (use_texture || !thick_line)
854
161k
        {
855
            // [PATH 1] Texture-based lines (thick or non-thick)
856
            // [PATH 2] Non texture-based lines (non-thick)
857
858
            // The width of the geometry we need to draw - this is essentially <thickness> pixels for the line itself, plus "one pixel" for AA.
859
            // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture
860
            //   (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code.
861
            // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to
862
            //   allow scaling geometry while preserving one-screen-pixel AA fringe).
863
161k
            const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE;
864
865
            // If line is not closed, the first and last points need to be generated differently as there are no normals to blend
866
161k
            if (!closed)
867
0
            {
868
0
                temp_points[0] = points[0] + temp_normals[0] * half_draw_size;
869
0
                temp_points[1] = points[0] - temp_normals[0] * half_draw_size;
870
0
                temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size;
871
0
                temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size;
872
0
            }
873
874
            // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges
875
            // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)
876
            // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
877
161k
            unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment
878
809k
            for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment
879
647k
            {
880
647k
                const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment
881
647k
                const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment
882
883
                // Average normals
884
647k
                float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
885
647k
                float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
886
647k
                IM_FIXNORMAL2F(dm_x, dm_y);
887
647k
                dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area
888
647k
                dm_y *= half_draw_size;
889
890
                // Add temporary vertices for the outer edges
891
647k
                ImVec2* out_vtx = &temp_points[i2 * 2];
892
647k
                out_vtx[0].x = points[i2].x + dm_x;
893
647k
                out_vtx[0].y = points[i2].y + dm_y;
894
647k
                out_vtx[1].x = points[i2].x - dm_x;
895
647k
                out_vtx[1].y = points[i2].y - dm_y;
896
897
647k
                if (use_texture)
898
647k
                {
899
                    // Add indices for two triangles
900
647k
                    _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri
901
647k
                    _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri
902
647k
                    _IdxWritePtr += 6;
903
647k
                }
904
0
                else
905
0
                {
906
                    // Add indexes for four triangles
907
0
                    _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1
908
0
                    _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2
909
0
                    _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1
910
0
                    _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2
911
0
                    _IdxWritePtr += 12;
912
0
                }
913
914
647k
                idx1 = idx2;
915
647k
            }
916
917
            // Add vertices for each point on the line
918
161k
            if (use_texture)
919
161k
            {
920
                // If we're using textures we only need to emit the left/right edge vertices
921
161k
                ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness];
922
                /*if (fractional_thickness != 0.0f) // Currently always zero when use_texture==false!
923
                {
924
                    const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1];
925
                    tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp()
926
                    tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness;
927
                    tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness;
928
                    tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness;
929
                }*/
930
161k
                ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y);
931
161k
                ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w);
932
809k
                for (int i = 0; i < points_count; i++)
933
647k
                {
934
647k
                    _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge
935
647k
                    _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge
936
647k
                    _VtxWritePtr += 2;
937
647k
                }
938
161k
            }
939
0
            else
940
0
            {
941
                // If we're not using a texture, we need the center vertex as well
942
0
                for (int i = 0; i < points_count; i++)
943
0
                {
944
0
                    _VtxWritePtr[0].pos = points[i];              _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;       // Center of line
945
0
                    _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge
946
0
                    _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge
947
0
                    _VtxWritePtr += 3;
948
0
                }
949
0
            }
950
161k
        }
951
659
        else
952
659
        {
953
            // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point
954
659
            const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
955
956
            // If line is not closed, the first and last points need to be generated differently as there are no normals to blend
957
659
            if (!closed)
958
659
            {
959
659
                const int points_last = points_count - 1;
960
659
                temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE);
961
659
                temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness);
962
659
                temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness);
963
659
                temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE);
964
659
                temp_points[points_last * 4 + 0] = points[points_last] + temp_normals[points_last] * (half_inner_thickness + AA_SIZE);
965
659
                temp_points[points_last * 4 + 1] = points[points_last] + temp_normals[points_last] * (half_inner_thickness);
966
659
                temp_points[points_last * 4 + 2] = points[points_last] - temp_normals[points_last] * (half_inner_thickness);
967
659
                temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE);
968
659
            }
969
970
            // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges
971
            // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)
972
            // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
973
659
            unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment
974
1.97k
            for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment
975
1.31k
            {
976
1.31k
                const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment
977
1.31k
                const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment
978
979
                // Average normals
980
1.31k
                float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
981
1.31k
                float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
982
1.31k
                IM_FIXNORMAL2F(dm_x, dm_y);
983
1.31k
                float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE);
984
1.31k
                float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE);
985
1.31k
                float dm_in_x = dm_x * half_inner_thickness;
986
1.31k
                float dm_in_y = dm_y * half_inner_thickness;
987
988
                // Add temporary vertices
989
1.31k
                ImVec2* out_vtx = &temp_points[i2 * 4];
990
1.31k
                out_vtx[0].x = points[i2].x + dm_out_x;
991
1.31k
                out_vtx[0].y = points[i2].y + dm_out_y;
992
1.31k
                out_vtx[1].x = points[i2].x + dm_in_x;
993
1.31k
                out_vtx[1].y = points[i2].y + dm_in_y;
994
1.31k
                out_vtx[2].x = points[i2].x - dm_in_x;
995
1.31k
                out_vtx[2].y = points[i2].y - dm_in_y;
996
1.31k
                out_vtx[3].x = points[i2].x - dm_out_x;
997
1.31k
                out_vtx[3].y = points[i2].y - dm_out_y;
998
999
                // Add indexes
1000
1.31k
                _IdxWritePtr[0]  = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[1]  = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[2]  = (ImDrawIdx)(idx1 + 2);
1001
1.31k
                _IdxWritePtr[3]  = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4]  = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5]  = (ImDrawIdx)(idx2 + 1);
1002
1.31k
                _IdxWritePtr[6]  = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7]  = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8]  = (ImDrawIdx)(idx1 + 0);
1003
1.31k
                _IdxWritePtr[9]  = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1);
1004
1.31k
                _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3);
1005
1.31k
                _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3); _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2);
1006
1.31k
                _IdxWritePtr += 18;
1007
1008
1.31k
                idx1 = idx2;
1009
1.31k
            }
1010
1011
            // Add vertices
1012
2.63k
            for (int i = 0; i < points_count; i++)
1013
1.97k
            {
1014
1.97k
                _VtxWritePtr[0].pos = temp_points[i * 4 + 0]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col_trans;
1015
1.97k
                _VtxWritePtr[1].pos = temp_points[i * 4 + 1]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;
1016
1.97k
                _VtxWritePtr[2].pos = temp_points[i * 4 + 2]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;
1017
1.97k
                _VtxWritePtr[3].pos = temp_points[i * 4 + 3]; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col_trans;
1018
1.97k
                _VtxWritePtr += 4;
1019
1.97k
            }
1020
659
        }
1021
162k
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
1022
162k
    }
1023
0
    else
1024
0
    {
1025
        // [PATH 4] Non texture-based, Non anti-aliased lines
1026
0
        const int idx_count = count * 6;
1027
0
        const int vtx_count = count * 4;    // FIXME-OPT: Not sharing edges
1028
0
        PrimReserve(idx_count, vtx_count);
1029
1030
0
        for (int i1 = 0; i1 < count; i1++)
1031
0
        {
1032
0
            const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;
1033
0
            const ImVec2& p1 = points[i1];
1034
0
            const ImVec2& p2 = points[i2];
1035
1036
0
            float dx = p2.x - p1.x;
1037
0
            float dy = p2.y - p1.y;
1038
0
            IM_NORMALIZE2F_OVER_ZERO(dx, dy);
1039
0
            dx *= (thickness * 0.5f);
1040
0
            dy *= (thickness * 0.5f);
1041
1042
0
            _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;
1043
0
            _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;
1044
0
            _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;
1045
0
            _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col;
1046
0
            _VtxWritePtr += 4;
1047
1048
0
            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2);
1049
0
            _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3);
1050
0
            _IdxWritePtr += 6;
1051
0
            _VtxCurrentIdx += 4;
1052
0
        }
1053
0
    }
1054
162k
}
1055
1056
// - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.
1057
// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing.
1058
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
1059
80.5k
{
1060
80.5k
    if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
1061
0
        return;
1062
1063
80.5k
    const ImVec2 uv = _Data->TexUvWhitePixel;
1064
1065
80.5k
    if (Flags & ImDrawListFlags_AntiAliasedFill)
1066
80.5k
    {
1067
        // Anti-aliased Fill
1068
80.5k
        const float AA_SIZE = _FringeScale;
1069
80.5k
        const ImU32 col_trans = col & ~IM_COL32_A_MASK;
1070
80.5k
        const int idx_count = (points_count - 2)*3 + points_count * 6;
1071
80.5k
        const int vtx_count = (points_count * 2);
1072
80.5k
        PrimReserve(idx_count, vtx_count);
1073
1074
        // Add indexes for fill
1075
80.5k
        unsigned int vtx_inner_idx = _VtxCurrentIdx;
1076
80.5k
        unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
1077
161k
        for (int i = 2; i < points_count; i++)
1078
80.5k
        {
1079
80.5k
            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1));
1080
80.5k
            _IdxWritePtr += 3;
1081
80.5k
        }
1082
1083
        // Compute normals
1084
80.5k
        _Data->TempBuffer.reserve_discard(points_count);
1085
80.5k
        ImVec2* temp_normals = _Data->TempBuffer.Data;
1086
322k
        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
1087
241k
        {
1088
241k
            const ImVec2& p0 = points[i0];
1089
241k
            const ImVec2& p1 = points[i1];
1090
241k
            float dx = p1.x - p0.x;
1091
241k
            float dy = p1.y - p0.y;
1092
241k
            IM_NORMALIZE2F_OVER_ZERO(dx, dy);
1093
241k
            temp_normals[i0].x = dy;
1094
241k
            temp_normals[i0].y = -dx;
1095
241k
        }
1096
1097
322k
        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
1098
241k
        {
1099
            // Average normals
1100
241k
            const ImVec2& n0 = temp_normals[i0];
1101
241k
            const ImVec2& n1 = temp_normals[i1];
1102
241k
            float dm_x = (n0.x + n1.x) * 0.5f;
1103
241k
            float dm_y = (n0.y + n1.y) * 0.5f;
1104
241k
            IM_FIXNORMAL2F(dm_x, dm_y);
1105
241k
            dm_x *= AA_SIZE * 0.5f;
1106
241k
            dm_y *= AA_SIZE * 0.5f;
1107
1108
            // Add vertices
1109
241k
            _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;        // Inner
1110
241k
            _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;  // Outer
1111
241k
            _VtxWritePtr += 2;
1112
1113
            // Add indexes for fringes
1114
241k
            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
1115
241k
            _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
1116
241k
            _IdxWritePtr += 6;
1117
241k
        }
1118
80.5k
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
1119
80.5k
    }
1120
0
    else
1121
0
    {
1122
        // Non Anti-aliased Fill
1123
0
        const int idx_count = (points_count - 2)*3;
1124
0
        const int vtx_count = points_count;
1125
0
        PrimReserve(idx_count, vtx_count);
1126
0
        for (int i = 0; i < vtx_count; i++)
1127
0
        {
1128
0
            _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
1129
0
            _VtxWritePtr++;
1130
0
        }
1131
0
        for (int i = 2; i < points_count; i++)
1132
0
        {
1133
0
            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i);
1134
0
            _IdxWritePtr += 3;
1135
0
        }
1136
0
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
1137
0
    }
1138
80.5k
}
1139
1140
void ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step)
1141
0
{
1142
0
    if (radius < 0.5f)
1143
0
    {
1144
0
        _Path.push_back(center);
1145
0
        return;
1146
0
    }
1147
1148
    // Calculate arc auto segment step size
1149
0
    if (a_step <= 0)
1150
0
        a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius);
1151
1152
    // Make sure we never do steps larger than one quarter of the circle
1153
0
    a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4);
1154
1155
0
    const int sample_range = ImAbs(a_max_sample - a_min_sample);
1156
0
    const int a_next_step = a_step;
1157
1158
0
    int samples = sample_range + 1;
1159
0
    bool extra_max_sample = false;
1160
0
    if (a_step > 1)
1161
0
    {
1162
0
        samples            = sample_range / a_step + 1;
1163
0
        const int overstep = sample_range % a_step;
1164
1165
0
        if (overstep > 0)
1166
0
        {
1167
0
            extra_max_sample = true;
1168
0
            samples++;
1169
1170
            // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end,
1171
            // distribute first step range evenly between them by reducing first step size.
1172
0
            if (sample_range > 0)
1173
0
                a_step -= (a_step - overstep) / 2;
1174
0
        }
1175
0
    }
1176
1177
0
    _Path.resize(_Path.Size + samples);
1178
0
    ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);
1179
1180
0
    int sample_index = a_min_sample;
1181
0
    if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
1182
0
    {
1183
0
        sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1184
0
        if (sample_index < 0)
1185
0
            sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1186
0
    }
1187
1188
0
    if (a_max_sample >= a_min_sample)
1189
0
    {
1190
0
        for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)
1191
0
        {
1192
            // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
1193
0
            if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)
1194
0
                sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1195
1196
0
            const ImVec2 s = _Data->ArcFastVtx[sample_index];
1197
0
            out_ptr->x = center.x + s.x * radius;
1198
0
            out_ptr->y = center.y + s.y * radius;
1199
0
            out_ptr++;
1200
0
        }
1201
0
    }
1202
0
    else
1203
0
    {
1204
0
        for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step)
1205
0
        {
1206
            // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more
1207
0
            if (sample_index < 0)
1208
0
                sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1209
1210
0
            const ImVec2 s = _Data->ArcFastVtx[sample_index];
1211
0
            out_ptr->x = center.x + s.x * radius;
1212
0
            out_ptr->y = center.y + s.y * radius;
1213
0
            out_ptr++;
1214
0
        }
1215
0
    }
1216
1217
0
    if (extra_max_sample)
1218
0
    {
1219
0
        int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1220
0
        if (normalized_max_sample < 0)
1221
0
            normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1222
1223
0
        const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample];
1224
0
        out_ptr->x = center.x + s.x * radius;
1225
0
        out_ptr->y = center.y + s.y * radius;
1226
0
        out_ptr++;
1227
0
    }
1228
1229
0
    IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr);
1230
0
}
1231
1232
void ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
1233
0
{
1234
0
    if (radius < 0.5f)
1235
0
    {
1236
0
        _Path.push_back(center);
1237
0
        return;
1238
0
    }
1239
1240
    // Note that we are adding a point at both a_min and a_max.
1241
    // If you are trying to draw a full closed circle you don't want the overlapping points!
1242
0
    _Path.reserve(_Path.Size + (num_segments + 1));
1243
0
    for (int i = 0; i <= num_segments; i++)
1244
0
    {
1245
0
        const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
1246
0
        _Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius));
1247
0
    }
1248
0
}
1249
1250
// 0: East, 3: South, 6: West, 9: North, 12: East
1251
void ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)
1252
0
{
1253
0
    if (radius < 0.5f)
1254
0
    {
1255
0
        _Path.push_back(center);
1256
0
        return;
1257
0
    }
1258
0
    _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0);
1259
0
}
1260
1261
void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)
1262
0
{
1263
0
    if (radius < 0.5f)
1264
0
    {
1265
0
        _Path.push_back(center);
1266
0
        return;
1267
0
    }
1268
1269
0
    if (num_segments > 0)
1270
0
    {
1271
0
        _PathArcToN(center, radius, a_min, a_max, num_segments);
1272
0
        return;
1273
0
    }
1274
1275
    // Automatic segment count
1276
0
    if (radius <= _Data->ArcFastRadiusCutoff)
1277
0
    {
1278
0
        const bool a_is_reverse = a_max < a_min;
1279
1280
        // We are going to use precomputed values for mid samples.
1281
        // Determine first and last sample in lookup table that belong to the arc.
1282
0
        const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);
1283
0
        const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);
1284
1285
0
        const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);
1286
0
        const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);
1287
0
        const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);
1288
1289
0
        const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1290
0
        const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;
1291
0
        const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f;
1292
0
        const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f;
1293
1294
0
        _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));
1295
0
        if (a_emit_start)
1296
0
            _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));
1297
0
        if (a_mid_samples > 0)
1298
0
            _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);
1299
0
        if (a_emit_end)
1300
0
            _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));
1301
0
    }
1302
0
    else
1303
0
    {
1304
0
        const float arc_length = ImAbs(a_max - a_min);
1305
0
        const int circle_segment_count = _CalcCircleAutoSegmentCount(radius);
1306
0
        const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length));
1307
0
        _PathArcToN(center, radius, a_min, a_max, arc_segment_count);
1308
0
    }
1309
0
}
1310
1311
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments)
1312
0
{
1313
0
    if (num_segments <= 0)
1314
0
        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
1315
1316
0
    _Path.reserve(_Path.Size + (num_segments + 1));
1317
1318
0
    const float cos_rot = ImCos(rot);
1319
0
    const float sin_rot = ImSin(rot);
1320
0
    for (int i = 0; i <= num_segments; i++)
1321
0
    {
1322
0
        const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
1323
0
        ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);
1324
0
        const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));
1325
0
        point.x = rel.x + center.x;
1326
0
        point.y = rel.y + center.y;
1327
0
        _Path.push_back(point);
1328
0
    }
1329
0
}
1330
1331
ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)
1332
0
{
1333
0
    float u = 1.0f - t;
1334
0
    float w1 = u * u * u;
1335
0
    float w2 = 3 * u * u * t;
1336
0
    float w3 = 3 * u * t * t;
1337
0
    float w4 = t * t * t;
1338
0
    return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y);
1339
0
}
1340
1341
ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t)
1342
0
{
1343
0
    float u = 1.0f - t;
1344
0
    float w1 = u * u;
1345
0
    float w2 = 2 * u * t;
1346
0
    float w3 = t * t;
1347
0
    return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y);
1348
0
}
1349
1350
// Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp
1351
static void PathBezierCubicCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
1352
0
{
1353
0
    float dx = x4 - x1;
1354
0
    float dy = y4 - y1;
1355
0
    float d2 = (x2 - x4) * dy - (y2 - y4) * dx;
1356
0
    float d3 = (x3 - x4) * dy - (y3 - y4) * dx;
1357
0
    d2 = (d2 >= 0) ? d2 : -d2;
1358
0
    d3 = (d3 >= 0) ? d3 : -d3;
1359
0
    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))
1360
0
    {
1361
0
        path->push_back(ImVec2(x4, y4));
1362
0
    }
1363
0
    else if (level < 10)
1364
0
    {
1365
0
        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;
1366
0
        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;
1367
0
        float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f;
1368
0
        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;
1369
0
        float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f;
1370
0
        float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f;
1371
0
        PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);
1372
0
        PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);
1373
0
    }
1374
0
}
1375
1376
static void PathBezierQuadraticCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level)
1377
0
{
1378
0
    float dx = x3 - x1, dy = y3 - y1;
1379
0
    float det = (x2 - x3) * dy - (y2 - y3) * dx;
1380
0
    if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy))
1381
0
    {
1382
0
        path->push_back(ImVec2(x3, y3));
1383
0
    }
1384
0
    else if (level < 10)
1385
0
    {
1386
0
        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;
1387
0
        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;
1388
0
        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;
1389
0
        PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1);
1390
0
        PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1);
1391
0
    }
1392
0
}
1393
1394
void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments)
1395
0
{
1396
0
    ImVec2 p1 = _Path.back();
1397
0
    if (num_segments == 0)
1398
0
    {
1399
0
        IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
1400
0
        PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated
1401
0
    }
1402
0
    else
1403
0
    {
1404
0
        float t_step = 1.0f / (float)num_segments;
1405
0
        for (int i_step = 1; i_step <= num_segments; i_step++)
1406
0
            _Path.push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step));
1407
0
    }
1408
0
}
1409
1410
void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments)
1411
0
{
1412
0
    ImVec2 p1 = _Path.back();
1413
0
    if (num_segments == 0)
1414
0
    {
1415
0
        IM_ASSERT(_Data->CurveTessellationTol > 0.0f);
1416
0
        PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated
1417
0
    }
1418
0
    else
1419
0
    {
1420
0
        float t_step = 1.0f / (float)num_segments;
1421
0
        for (int i_step = 1; i_step <= num_segments; i_step++)
1422
0
            _Path.push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step));
1423
0
    }
1424
0
}
1425
1426
static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)
1427
0
{
1428
    /*
1429
    IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));
1430
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1431
    // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)
1432
    // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)
1433
    if (flags == ~0)                    { return ImDrawFlags_RoundCornersAll; }
1434
    // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.
1435
    if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }
1436
    // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'
1437
#endif
1438
    */
1439
    // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.
1440
    // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.
1441
    // See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section.
1442
0
    IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!");
1443
1444
0
    if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
1445
0
        flags |= ImDrawFlags_RoundCornersAll;
1446
1447
0
    return flags;
1448
0
}
1449
1450
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)
1451
161k
{
1452
161k
    if (rounding >= 0.5f)
1453
0
    {
1454
0
        flags = FixRectCornerFlags(flags);
1455
0
        rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);
1456
0
        rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);
1457
0
    }
1458
161k
    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
1459
161k
    {
1460
161k
        PathLineTo(a);
1461
161k
        PathLineTo(ImVec2(b.x, a.y));
1462
161k
        PathLineTo(b);
1463
161k
        PathLineTo(ImVec2(a.x, b.y));
1464
161k
    }
1465
0
    else
1466
0
    {
1467
0
        const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft)     ? rounding : 0.0f;
1468
0
        const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight)    ? rounding : 0.0f;
1469
0
        const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f;
1470
0
        const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft)  ? rounding : 0.0f;
1471
0
        PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9);
1472
0
        PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12);
1473
0
        PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3);
1474
0
        PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6);
1475
0
    }
1476
161k
}
1477
1478
void ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness)
1479
0
{
1480
0
    if ((col & IM_COL32_A_MASK) == 0)
1481
0
        return;
1482
0
    PathLineTo(p1 + ImVec2(0.5f, 0.5f));
1483
0
    PathLineTo(p2 + ImVec2(0.5f, 0.5f));
1484
0
    PathStroke(col, 0, thickness);
1485
0
}
1486
1487
// p_min = upper-left, p_max = lower-right
1488
// Note we don't render 1 pixels sized rectangles properly.
1489
void ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness)
1490
161k
{
1491
161k
    if ((col & IM_COL32_A_MASK) == 0)
1492
0
        return;
1493
161k
    if (Flags & ImDrawListFlags_AntiAliasedLines)
1494
161k
        PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags);
1495
0
    else
1496
0
        PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes.
1497
161k
    PathStroke(col, ImDrawFlags_Closed, thickness);
1498
161k
}
1499
1500
void ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags)
1501
324k
{
1502
324k
    if ((col & IM_COL32_A_MASK) == 0)
1503
0
        return;
1504
324k
    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
1505
324k
    {
1506
324k
        PrimReserve(6, 4);
1507
324k
        PrimRect(p_min, p_max, col);
1508
324k
    }
1509
0
    else
1510
0
    {
1511
0
        PathRect(p_min, p_max, rounding, flags);
1512
0
        PathFillConvex(col);
1513
0
    }
1514
324k
}
1515
1516
// p_min = upper-left, p_max = lower-right
1517
void ImDrawList::AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left)
1518
0
{
1519
0
    if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0)
1520
0
        return;
1521
1522
0
    const ImVec2 uv = _Data->TexUvWhitePixel;
1523
0
    PrimReserve(6, 4);
1524
0
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));
1525
0
    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3));
1526
0
    PrimWriteVtx(p_min, uv, col_upr_left);
1527
0
    PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right);
1528
0
    PrimWriteVtx(p_max, uv, col_bot_right);
1529
0
    PrimWriteVtx(ImVec2(p_min.x, p_max.y), uv, col_bot_left);
1530
0
}
1531
1532
void ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness)
1533
0
{
1534
0
    if ((col & IM_COL32_A_MASK) == 0)
1535
0
        return;
1536
1537
0
    PathLineTo(p1);
1538
0
    PathLineTo(p2);
1539
0
    PathLineTo(p3);
1540
0
    PathLineTo(p4);
1541
0
    PathStroke(col, ImDrawFlags_Closed, thickness);
1542
0
}
1543
1544
void ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col)
1545
0
{
1546
0
    if ((col & IM_COL32_A_MASK) == 0)
1547
0
        return;
1548
1549
0
    PathLineTo(p1);
1550
0
    PathLineTo(p2);
1551
0
    PathLineTo(p3);
1552
0
    PathLineTo(p4);
1553
0
    PathFillConvex(col);
1554
0
}
1555
1556
void ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness)
1557
0
{
1558
0
    if ((col & IM_COL32_A_MASK) == 0)
1559
0
        return;
1560
1561
0
    PathLineTo(p1);
1562
0
    PathLineTo(p2);
1563
0
    PathLineTo(p3);
1564
0
    PathStroke(col, ImDrawFlags_Closed, thickness);
1565
0
}
1566
1567
void ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col)
1568
80.5k
{
1569
80.5k
    if ((col & IM_COL32_A_MASK) == 0)
1570
0
        return;
1571
1572
80.5k
    PathLineTo(p1);
1573
80.5k
    PathLineTo(p2);
1574
80.5k
    PathLineTo(p3);
1575
80.5k
    PathFillConvex(col);
1576
80.5k
}
1577
1578
void ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)
1579
0
{
1580
0
    if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)
1581
0
        return;
1582
1583
0
    if (num_segments <= 0)
1584
0
    {
1585
        // Use arc with automatic segment count
1586
0
        _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);
1587
0
        _Path.Size--;
1588
0
    }
1589
0
    else
1590
0
    {
1591
        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
1592
0
        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);
1593
1594
        // Because we are filling a closed shape we remove 1 from the count of segments/points
1595
0
        const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
1596
0
        PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
1597
0
    }
1598
1599
0
    PathStroke(col, ImDrawFlags_Closed, thickness);
1600
0
}
1601
1602
void ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
1603
0
{
1604
0
    if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)
1605
0
        return;
1606
1607
0
    if (num_segments <= 0)
1608
0
    {
1609
        // Use arc with automatic segment count
1610
0
        _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);
1611
0
        _Path.Size--;
1612
0
    }
1613
0
    else
1614
0
    {
1615
        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)
1616
0
        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);
1617
1618
        // Because we are filling a closed shape we remove 1 from the count of segments/points
1619
0
        const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
1620
0
        PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);
1621
0
    }
1622
1623
0
    PathFillConvex(col);
1624
0
}
1625
1626
// Guaranteed to honor 'num_segments'
1627
void ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)
1628
0
{
1629
0
    if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)
1630
0
        return;
1631
1632
    // Because we are filling a closed shape we remove 1 from the count of segments/points
1633
0
    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
1634
0
    PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);
1635
0
    PathStroke(col, ImDrawFlags_Closed, thickness);
1636
0
}
1637
1638
// Guaranteed to honor 'num_segments'
1639
void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)
1640
0
{
1641
0
    if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)
1642
0
        return;
1643
1644
    // Because we are filling a closed shape we remove 1 from the count of segments/points
1645
0
    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;
1646
0
    PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);
1647
0
    PathFillConvex(col);
1648
0
}
1649
1650
// Ellipse
1651
void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness)
1652
0
{
1653
0
    if ((col & IM_COL32_A_MASK) == 0)
1654
0
        return;
1655
1656
0
    if (num_segments <= 0)
1657
0
        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
1658
1659
    // Because we are filling a closed shape we remove 1 from the count of segments/points
1660
0
    const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
1661
0
    PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
1662
0
    PathStroke(col, true, thickness);
1663
0
}
1664
1665
void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)
1666
0
{
1667
0
    if ((col & IM_COL32_A_MASK) == 0)
1668
0
        return;
1669
1670
0
    if (num_segments <= 0)
1671
0
        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
1672
1673
    // Because we are filling a closed shape we remove 1 from the count of segments/points
1674
0
    const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
1675
0
    PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
1676
0
    PathFillConvex(col);
1677
0
}
1678
1679
// Cubic Bezier takes 4 controls points
1680
void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)
1681
0
{
1682
0
    if ((col & IM_COL32_A_MASK) == 0)
1683
0
        return;
1684
1685
0
    PathLineTo(p1);
1686
0
    PathBezierCubicCurveTo(p2, p3, p4, num_segments);
1687
0
    PathStroke(col, 0, thickness);
1688
0
}
1689
1690
// Quadratic Bezier takes 3 controls points
1691
void ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments)
1692
0
{
1693
0
    if ((col & IM_COL32_A_MASK) == 0)
1694
0
        return;
1695
1696
0
    PathLineTo(p1);
1697
0
    PathBezierQuadraticCurveTo(p2, p3, num_segments);
1698
0
    PathStroke(col, 0, thickness);
1699
0
}
1700
1701
void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
1702
324k
{
1703
324k
    if ((col & IM_COL32_A_MASK) == 0)
1704
0
        return;
1705
1706
    // Accept null ranges
1707
324k
    if (text_begin == text_end || text_begin[0] == 0)
1708
0
        return;
1709
    // No need to strlen() here: font->RenderText() will do it and may early out.
1710
1711
    // Pull default font/size from the shared ImDrawListSharedData instance
1712
324k
    if (font == NULL)
1713
80.5k
        font = _Data->Font;
1714
324k
    if (font_size == 0.0f)
1715
80.5k
        font_size = _Data->FontSize;
1716
1717
324k
    ImVec4 clip_rect = _CmdHeader.ClipRect;
1718
324k
    if (cpu_fine_clip_rect)
1719
0
    {
1720
0
        clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);
1721
0
        clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y);
1722
0
        clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
1723
0
        clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
1724
0
    }
1725
324k
    font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, (cpu_fine_clip_rect != NULL) ? ImDrawTextFlags_CpuFineClip : ImDrawTextFlags_None);
1726
324k
}
1727
1728
void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
1729
0
{
1730
0
    AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end);
1731
0
}
1732
1733
void ImDrawList::AddImage(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)
1734
0
{
1735
0
    if ((col & IM_COL32_A_MASK) == 0)
1736
0
        return;
1737
1738
0
    const bool push_texture_id = tex_ref != _CmdHeader.TexRef;
1739
0
    if (push_texture_id)
1740
0
        PushTexture(tex_ref);
1741
1742
0
    PrimReserve(6, 4);
1743
0
    PrimRectUV(p_min, p_max, uv_min, uv_max, col);
1744
1745
0
    if (push_texture_id)
1746
0
        PopTexture();
1747
0
}
1748
1749
void ImDrawList::AddImageQuad(ImTextureRef tex_ref, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col)
1750
0
{
1751
0
    if ((col & IM_COL32_A_MASK) == 0)
1752
0
        return;
1753
1754
0
    const bool push_texture_id = tex_ref != _CmdHeader.TexRef;
1755
0
    if (push_texture_id)
1756
0
        PushTexture(tex_ref);
1757
1758
0
    PrimReserve(6, 4);
1759
0
    PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col);
1760
1761
0
    if (push_texture_id)
1762
0
        PopTexture();
1763
0
}
1764
1765
void ImDrawList::AddImageRounded(ImTextureRef tex_ref, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags)
1766
0
{
1767
0
    if ((col & IM_COL32_A_MASK) == 0)
1768
0
        return;
1769
1770
0
    flags = FixRectCornerFlags(flags);
1771
0
    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)
1772
0
    {
1773
0
        AddImage(tex_ref, p_min, p_max, uv_min, uv_max, col);
1774
0
        return;
1775
0
    }
1776
1777
0
    const bool push_texture_id = tex_ref != _CmdHeader.TexRef;
1778
0
    if (push_texture_id)
1779
0
        PushTexture(tex_ref);
1780
1781
0
    int vert_start_idx = VtxBuffer.Size;
1782
0
    PathRect(p_min, p_max, rounding, flags);
1783
0
    PathFillConvex(col);
1784
0
    int vert_end_idx = VtxBuffer.Size;
1785
0
    ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true);
1786
1787
0
    if (push_texture_id)
1788
0
        PopTexture();
1789
0
}
1790
1791
//-----------------------------------------------------------------------------
1792
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
1793
//-----------------------------------------------------------------------------
1794
// Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
1795
// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
1796
// Provided as a convenience for user but not used by main library.
1797
//-----------------------------------------------------------------------------
1798
// - ImTriangulator [Internal]
1799
// - AddConcavePolyFilled()
1800
//-----------------------------------------------------------------------------
1801
1802
enum ImTriangulatorNodeType
1803
{
1804
    ImTriangulatorNodeType_Convex,
1805
    ImTriangulatorNodeType_Ear,
1806
    ImTriangulatorNodeType_Reflex
1807
};
1808
1809
struct ImTriangulatorNode
1810
{
1811
    ImTriangulatorNodeType  Type;
1812
    int                     Index;
1813
    ImVec2                  Pos;
1814
    ImTriangulatorNode*     Next;
1815
    ImTriangulatorNode*     Prev;
1816
1817
0
    void    Unlink()        { Next->Prev = Prev; Prev->Next = Next; }
1818
};
1819
1820
struct ImTriangulatorNodeSpan
1821
{
1822
    ImTriangulatorNode**    Data = NULL;
1823
    int                     Size = 0;
1824
1825
0
    void    push_back(ImTriangulatorNode* node) { Data[Size++] = node; }
1826
0
    void    find_erase_unsorted(int idx)        { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } }
1827
};
1828
1829
struct ImTriangulator
1830
{
1831
0
    static int EstimateTriangleCount(int points_count)      { return (points_count < 3) ? 0 : points_count - 2; }
1832
0
    static int EstimateScratchBufferSize(int points_count)  { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; }
1833
1834
    void    Init(const ImVec2* points, int points_count, void* scratch_buffer);
1835
    void    GetNextTriangle(unsigned int out_triangle[3]);     // Return relative indexes for next triangle
1836
1837
    // Internal functions
1838
    void    BuildNodes(const ImVec2* points, int points_count);
1839
    void    BuildReflexes();
1840
    void    BuildEars();
1841
    void    FlipNodeList();
1842
    bool    IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;
1843
    void    ReclassifyNode(ImTriangulatorNode* node);
1844
1845
    // Internal members
1846
    int                     _TrianglesLeft = 0;
1847
    ImTriangulatorNode*     _Nodes = NULL;
1848
    ImTriangulatorNodeSpan  _Ears;
1849
    ImTriangulatorNodeSpan  _Reflexes;
1850
};
1851
1852
// Distribute storage for nodes, ears and reflexes.
1853
// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
1854
// (this would require first building reflexes to bail to convex if empty, without even building nodes)
1855
void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer)
1856
0
{
1857
0
    IM_ASSERT(scratch_buffer != NULL && points_count >= 3);
1858
0
    _TrianglesLeft = EstimateTriangleCount(points_count);
1859
0
    _Nodes         = (ImTriangulatorNode*)scratch_buffer;                          // points_count x Node
1860
0
    _Ears.Data     = (ImTriangulatorNode**)(_Nodes + points_count);                // points_count x Node*
1861
0
    _Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node*
1862
0
    BuildNodes(points, points_count);
1863
0
    BuildReflexes();
1864
0
    BuildEars();
1865
0
}
1866
1867
void ImTriangulator::BuildNodes(const ImVec2* points, int points_count)
1868
0
{
1869
0
    for (int i = 0; i < points_count; i++)
1870
0
    {
1871
0
        _Nodes[i].Type = ImTriangulatorNodeType_Convex;
1872
0
        _Nodes[i].Index = i;
1873
0
        _Nodes[i].Pos = points[i];
1874
0
        _Nodes[i].Next = _Nodes + i + 1;
1875
0
        _Nodes[i].Prev = _Nodes + i - 1;
1876
0
    }
1877
0
    _Nodes[0].Prev = _Nodes + points_count - 1;
1878
0
    _Nodes[points_count - 1].Next = _Nodes;
1879
0
}
1880
1881
void ImTriangulator::BuildReflexes()
1882
0
{
1883
0
    ImTriangulatorNode* n1 = _Nodes;
1884
0
    for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
1885
0
    {
1886
0
        if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos))
1887
0
            continue;
1888
0
        n1->Type = ImTriangulatorNodeType_Reflex;
1889
0
        _Reflexes.push_back(n1);
1890
0
    }
1891
0
}
1892
1893
void ImTriangulator::BuildEars()
1894
0
{
1895
0
    ImTriangulatorNode* n1 = _Nodes;
1896
0
    for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
1897
0
    {
1898
0
        if (n1->Type != ImTriangulatorNodeType_Convex)
1899
0
            continue;
1900
0
        if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos))
1901
0
            continue;
1902
0
        n1->Type = ImTriangulatorNodeType_Ear;
1903
0
        _Ears.push_back(n1);
1904
0
    }
1905
0
}
1906
1907
void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3])
1908
0
{
1909
0
    if (_Ears.Size == 0)
1910
0
    {
1911
0
        FlipNodeList();
1912
1913
0
        ImTriangulatorNode* node = _Nodes;
1914
0
        for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next)
1915
0
            node->Type = ImTriangulatorNodeType_Convex;
1916
0
        _Reflexes.Size = 0;
1917
0
        BuildReflexes();
1918
0
        BuildEars();
1919
1920
        // If we still don't have ears, it means geometry is degenerated.
1921
0
        if (_Ears.Size == 0)
1922
0
        {
1923
            // Return first triangle available, mimicking the behavior of convex fill.
1924
0
            IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated
1925
0
            _Ears.Data[0] = _Nodes;
1926
0
            _Ears.Size    = 1;
1927
0
        }
1928
0
    }
1929
1930
0
    ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size];
1931
0
    out_triangle[0] = ear->Prev->Index;
1932
0
    out_triangle[1] = ear->Index;
1933
0
    out_triangle[2] = ear->Next->Index;
1934
1935
0
    ear->Unlink();
1936
0
    if (ear == _Nodes)
1937
0
        _Nodes = ear->Next;
1938
1939
0
    ReclassifyNode(ear->Prev);
1940
0
    ReclassifyNode(ear->Next);
1941
0
    _TrianglesLeft--;
1942
0
}
1943
1944
void ImTriangulator::FlipNodeList()
1945
0
{
1946
0
    ImTriangulatorNode* prev = _Nodes;
1947
0
    ImTriangulatorNode* temp = _Nodes;
1948
0
    ImTriangulatorNode* current = _Nodes->Next;
1949
0
    prev->Next = prev;
1950
0
    prev->Prev = prev;
1951
0
    while (current != _Nodes)
1952
0
    {
1953
0
        temp = current->Next;
1954
1955
0
        current->Next = prev;
1956
0
        prev->Prev = current;
1957
0
        _Nodes->Next = current;
1958
0
        current->Prev = _Nodes;
1959
1960
0
        prev = current;
1961
0
        current = temp;
1962
0
    }
1963
0
    _Nodes = prev;
1964
0
}
1965
1966
// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
1967
bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const
1968
0
{
1969
0
    ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size;
1970
0
    for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++)
1971
0
    {
1972
0
        ImTriangulatorNode* reflex = *p;
1973
0
        if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2)
1974
0
            if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos))
1975
0
                return false;
1976
0
    }
1977
0
    return true;
1978
0
}
1979
1980
void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1)
1981
0
{
1982
    // Classify node
1983
0
    ImTriangulatorNodeType type;
1984
0
    const ImTriangulatorNode* n0 = n1->Prev;
1985
0
    const ImTriangulatorNode* n2 = n1->Next;
1986
0
    if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos))
1987
0
        type = ImTriangulatorNodeType_Reflex;
1988
0
    else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos))
1989
0
        type = ImTriangulatorNodeType_Ear;
1990
0
    else
1991
0
        type = ImTriangulatorNodeType_Convex;
1992
1993
    // Update lists when a type changes
1994
0
    if (type == n1->Type)
1995
0
        return;
1996
0
    if (n1->Type == ImTriangulatorNodeType_Reflex)
1997
0
        _Reflexes.find_erase_unsorted(n1->Index);
1998
0
    else if (n1->Type == ImTriangulatorNodeType_Ear)
1999
0
        _Ears.find_erase_unsorted(n1->Index);
2000
0
    if (type == ImTriangulatorNodeType_Reflex)
2001
0
        _Reflexes.push_back(n1);
2002
0
    else if (type == ImTriangulatorNodeType_Ear)
2003
0
        _Ears.push_back(n1);
2004
0
    n1->Type = type;
2005
0
}
2006
2007
// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
2008
// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
2009
// It is up to caller to ensure not making costly calls that will be outside of visible area.
2010
// As concave fill is noticeably more expensive than other primitives, be mindful of this...
2011
// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
2012
void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)
2013
0
{
2014
0
    if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
2015
0
        return;
2016
2017
0
    const ImVec2 uv = _Data->TexUvWhitePixel;
2018
0
    ImTriangulator triangulator;
2019
0
    unsigned int triangle[3];
2020
0
    if (Flags & ImDrawListFlags_AntiAliasedFill)
2021
0
    {
2022
        // Anti-aliased Fill
2023
0
        const float AA_SIZE = _FringeScale;
2024
0
        const ImU32 col_trans = col & ~IM_COL32_A_MASK;
2025
0
        const int idx_count = (points_count - 2) * 3 + points_count * 6;
2026
0
        const int vtx_count = (points_count * 2);
2027
0
        PrimReserve(idx_count, vtx_count);
2028
2029
        // Add indexes for fill
2030
0
        unsigned int vtx_inner_idx = _VtxCurrentIdx;
2031
0
        unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
2032
2033
0
        _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
2034
0
        triangulator.Init(points, points_count, _Data->TempBuffer.Data);
2035
0
        while (triangulator._TrianglesLeft > 0)
2036
0
        {
2037
0
            triangulator.GetNextTriangle(triangle);
2038
0
            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1));
2039
0
            _IdxWritePtr += 3;
2040
0
        }
2041
2042
        // Compute normals
2043
0
        _Data->TempBuffer.reserve_discard(points_count);
2044
0
        ImVec2* temp_normals = _Data->TempBuffer.Data;
2045
0
        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
2046
0
        {
2047
0
            const ImVec2& p0 = points[i0];
2048
0
            const ImVec2& p1 = points[i1];
2049
0
            float dx = p1.x - p0.x;
2050
0
            float dy = p1.y - p0.y;
2051
0
            IM_NORMALIZE2F_OVER_ZERO(dx, dy);
2052
0
            temp_normals[i0].x = dy;
2053
0
            temp_normals[i0].y = -dx;
2054
0
        }
2055
2056
0
        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
2057
0
        {
2058
            // Average normals
2059
0
            const ImVec2& n0 = temp_normals[i0];
2060
0
            const ImVec2& n1 = temp_normals[i1];
2061
0
            float dm_x = (n0.x + n1.x) * 0.5f;
2062
0
            float dm_y = (n0.y + n1.y) * 0.5f;
2063
0
            IM_FIXNORMAL2F(dm_x, dm_y);
2064
0
            dm_x *= AA_SIZE * 0.5f;
2065
0
            dm_y *= AA_SIZE * 0.5f;
2066
2067
            // Add vertices
2068
0
            _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;        // Inner
2069
0
            _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;  // Outer
2070
0
            _VtxWritePtr += 2;
2071
2072
            // Add indexes for fringes
2073
0
            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
2074
0
            _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
2075
0
            _IdxWritePtr += 6;
2076
0
        }
2077
0
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
2078
0
    }
2079
0
    else
2080
0
    {
2081
        // Non Anti-aliased Fill
2082
0
        const int idx_count = (points_count - 2) * 3;
2083
0
        const int vtx_count = points_count;
2084
0
        PrimReserve(idx_count, vtx_count);
2085
0
        for (int i = 0; i < vtx_count; i++)
2086
0
        {
2087
0
            _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
2088
0
            _VtxWritePtr++;
2089
0
        }
2090
0
        _Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
2091
0
        triangulator.Init(points, points_count, _Data->TempBuffer.Data);
2092
0
        while (triangulator._TrianglesLeft > 0)
2093
0
        {
2094
0
            triangulator.GetNextTriangle(triangle);
2095
0
            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]);
2096
0
            _IdxWritePtr += 3;
2097
0
        }
2098
0
        _VtxCurrentIdx += (ImDrawIdx)vtx_count;
2099
0
    }
2100
0
}
2101
2102
//-----------------------------------------------------------------------------
2103
// [SECTION] ImDrawListSplitter
2104
//-----------------------------------------------------------------------------
2105
// FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap..
2106
//-----------------------------------------------------------------------------
2107
2108
void ImDrawListSplitter::ClearFreeMemory()
2109
7
{
2110
7
    for (int i = 0; i < _Channels.Size; i++)
2111
0
    {
2112
0
        if (i == _Current)
2113
0
            memset(&_Channels[i], 0, sizeof(_Channels[i]));  // Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again
2114
0
        _Channels[i]._CmdBuffer.clear();
2115
0
        _Channels[i]._IdxBuffer.clear();
2116
0
    }
2117
7
    _Current = 0;
2118
7
    _Count = 1;
2119
7
    _Channels.clear();
2120
7
}
2121
2122
void ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count)
2123
0
{
2124
0
    IM_UNUSED(draw_list);
2125
0
    IM_ASSERT(_Current == 0 && _Count <= 1 && "Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.");
2126
0
    int old_channels_count = _Channels.Size;
2127
0
    if (old_channels_count < channels_count)
2128
0
    {
2129
0
        _Channels.reserve(channels_count); // Avoid over reserving since this is likely to stay stable
2130
0
        _Channels.resize(channels_count);
2131
0
    }
2132
0
    _Count = channels_count;
2133
2134
    // Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer
2135
    // The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to.
2136
    // When we switch to the next channel, we'll copy draw_list->_CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list->CmdBuffer/_IdxBuffer
2137
0
    memset(&_Channels[0], 0, sizeof(ImDrawChannel));
2138
0
    for (int i = 1; i < channels_count; i++)
2139
0
    {
2140
0
        if (i >= old_channels_count)
2141
0
        {
2142
0
            IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel();
2143
0
        }
2144
0
        else
2145
0
        {
2146
0
            _Channels[i]._CmdBuffer.resize(0);
2147
0
            _Channels[i]._IdxBuffer.resize(0);
2148
0
        }
2149
0
    }
2150
0
}
2151
2152
void ImDrawListSplitter::Merge(ImDrawList* draw_list)
2153
0
{
2154
    // Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
2155
0
    if (_Count <= 1)
2156
0
        return;
2157
2158
0
    SetCurrentChannel(draw_list, 0);
2159
0
    draw_list->_PopUnusedDrawCmd();
2160
2161
    // Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command.
2162
0
    int new_cmd_buffer_count = 0;
2163
0
    int new_idx_buffer_count = 0;
2164
0
    ImDrawCmd* last_cmd = (_Count > 0 && draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.back() : NULL;
2165
0
    int idx_offset = last_cmd ? last_cmd->IdxOffset + last_cmd->ElemCount : 0;
2166
0
    for (int i = 1; i < _Count; i++)
2167
0
    {
2168
0
        ImDrawChannel& ch = _Channels[i];
2169
0
        if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
2170
0
            ch._CmdBuffer.pop_back();
2171
2172
0
        if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)
2173
0
        {
2174
            // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.
2175
            // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.
2176
0
            ImDrawCmd* next_cmd = &ch._CmdBuffer[0];
2177
0
            if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL)
2178
0
            {
2179
                // Merge previous channel last draw command with current channel first draw command if matching.
2180
0
                last_cmd->ElemCount += next_cmd->ElemCount;
2181
0
                idx_offset += next_cmd->ElemCount;
2182
0
                ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges.
2183
0
            }
2184
0
        }
2185
0
        if (ch._CmdBuffer.Size > 0)
2186
0
            last_cmd = &ch._CmdBuffer.back();
2187
0
        new_cmd_buffer_count += ch._CmdBuffer.Size;
2188
0
        new_idx_buffer_count += ch._IdxBuffer.Size;
2189
0
        for (int cmd_n = 0; cmd_n < ch._CmdBuffer.Size; cmd_n++)
2190
0
        {
2191
0
            ch._CmdBuffer.Data[cmd_n].IdxOffset = idx_offset;
2192
0
            idx_offset += ch._CmdBuffer.Data[cmd_n].ElemCount;
2193
0
        }
2194
0
    }
2195
0
    draw_list->CmdBuffer.resize(draw_list->CmdBuffer.Size + new_cmd_buffer_count);
2196
0
    draw_list->IdxBuffer.resize(draw_list->IdxBuffer.Size + new_idx_buffer_count);
2197
2198
    // Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices)
2199
0
    ImDrawCmd* cmd_write = draw_list->CmdBuffer.Data + draw_list->CmdBuffer.Size - new_cmd_buffer_count;
2200
0
    ImDrawIdx* idx_write = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size - new_idx_buffer_count;
2201
0
    for (int i = 1; i < _Count; i++)
2202
0
    {
2203
0
        ImDrawChannel& ch = _Channels[i];
2204
0
        if (int sz = ch._CmdBuffer.Size) { memcpy(cmd_write, ch._CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; }
2205
0
        if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; }
2206
0
    }
2207
0
    draw_list->_IdxWritePtr = idx_write;
2208
2209
    // Ensure there's always a non-callback draw command trailing the command-buffer
2210
0
    if (draw_list->CmdBuffer.Size == 0 || draw_list->CmdBuffer.back().UserCallback != NULL)
2211
0
        draw_list->AddDrawCmd();
2212
2213
    // If current command is used with different settings we need to add a new command
2214
0
    ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
2215
0
    if (curr_cmd->ElemCount == 0)
2216
0
        ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset
2217
0
    else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
2218
0
        draw_list->AddDrawCmd();
2219
2220
0
    _Count = 1;
2221
0
}
2222
2223
void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)
2224
0
{
2225
0
    IM_ASSERT(idx >= 0 && idx < _Count);
2226
0
    if (_Current == idx)
2227
0
        return;
2228
2229
    // Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()
2230
0
    memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer));
2231
0
    memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer));
2232
0
    _Current = idx;
2233
0
    memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer));
2234
0
    memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer));
2235
0
    draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size;
2236
2237
    // If current command is used with different settings we need to add a new command
2238
0
    ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];
2239
0
    if (curr_cmd == NULL)
2240
0
        draw_list->AddDrawCmd();
2241
0
    else if (curr_cmd->ElemCount == 0)
2242
0
        ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TexRef, VtxOffset
2243
0
    else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)
2244
0
        draw_list->AddDrawCmd();
2245
0
}
2246
2247
//-----------------------------------------------------------------------------
2248
// [SECTION] ImDrawData
2249
//-----------------------------------------------------------------------------
2250
2251
void ImDrawData::Clear()
2252
1
{
2253
1
    Valid = false;
2254
1
    CmdListsCount = TotalIdxCount = TotalVtxCount = 0;
2255
1
    CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.
2256
1
    DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);
2257
1
    OwnerViewport = NULL;
2258
1
    Textures = NULL;
2259
1
}
2260
2261
// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list
2262
// as long at it is expected that the result will be later merged into draw_data->CmdLists[].
2263
void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)
2264
81.2k
{
2265
81.2k
    if (draw_list->CmdBuffer.Size == 0)
2266
0
        return;
2267
81.2k
    if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)
2268
0
        return;
2269
2270
    // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.
2271
    // May trigger for you if you are using PrimXXX functions incorrectly.
2272
81.2k
    IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);
2273
81.2k
    IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);
2274
81.2k
    if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))
2275
81.2k
        IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);
2276
2277
    // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)
2278
    // If this assert triggers because you are drawing lots of stuff manually:
2279
    // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.
2280
    //   Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.
2281
    // - If you want large meshes with more than 64K vertices, you can either:
2282
    //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.
2283
    //       Most example backends already support this from 1.71. Pre-1.71 backends won't.
2284
    //       Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.
2285
    //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.
2286
    //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:
2287
    //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);
2288
    //       Your own engine or render API may use different parameters or function calls to specify index sizes.
2289
    //       2 and 4 bytes indices are generally supported by most graphics API.
2290
    // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching
2291
    //   the 64K limit to split your draw commands in multiple draw lists.
2292
81.2k
    if (sizeof(ImDrawIdx) == 2)
2293
81.2k
        IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above");
2294
2295
    // Resolve callback data pointers
2296
81.2k
    if (draw_list->_CallbacksDataBuf.Size > 0)
2297
0
        for (ImDrawCmd& cmd : draw_list->CmdBuffer)
2298
0
            if (cmd.UserCallback != NULL && cmd.UserCallbackDataOffset != -1 && cmd.UserCallbackDataSize > 0)
2299
0
                cmd.UserCallbackData = draw_list->_CallbacksDataBuf.Data + cmd.UserCallbackDataOffset;
2300
2301
    // Add to output list + records state in ImDrawData
2302
81.2k
    out_list->push_back(draw_list);
2303
81.2k
    draw_data->CmdListsCount++;
2304
81.2k
    draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;
2305
81.2k
    draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;
2306
81.2k
}
2307
2308
void ImDrawData::AddDrawList(ImDrawList* draw_list)
2309
0
{
2310
0
    IM_ASSERT(CmdLists.Size == CmdListsCount);
2311
0
    draw_list->_PopUnusedDrawCmd();
2312
0
    ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);
2313
0
}
2314
2315
// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
2316
void ImDrawData::DeIndexAllBuffers()
2317
0
{
2318
0
    ImVector<ImDrawVert> new_vtx_buffer;
2319
0
    TotalVtxCount = TotalIdxCount = 0;
2320
0
    for (ImDrawList* draw_list : CmdLists)
2321
0
    {
2322
0
        if (draw_list->IdxBuffer.empty())
2323
0
            continue;
2324
0
        new_vtx_buffer.resize(draw_list->IdxBuffer.Size);
2325
0
        for (int j = 0; j < draw_list->IdxBuffer.Size; j++)
2326
0
            new_vtx_buffer[j] = draw_list->VtxBuffer[draw_list->IdxBuffer[j]];
2327
0
        draw_list->VtxBuffer.swap(new_vtx_buffer);
2328
0
        draw_list->IdxBuffer.resize(0);
2329
0
        TotalVtxCount += draw_list->VtxBuffer.Size;
2330
0
    }
2331
0
}
2332
2333
// Helper to scale the ClipRect field of each ImDrawCmd.
2334
// Use if your final output buffer is at a different scale than draw_data->DisplaySize,
2335
// or if there is a difference between your window resolution and framebuffer resolution.
2336
void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
2337
0
{
2338
0
    for (ImDrawList* draw_list : CmdLists)
2339
0
        for (ImDrawCmd& cmd : draw_list->CmdBuffer)
2340
0
            cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);
2341
0
}
2342
2343
//-----------------------------------------------------------------------------
2344
// [SECTION] Helpers ShadeVertsXXX functions
2345
//-----------------------------------------------------------------------------
2346
2347
// Generic linear color gradient, write to RGB fields, leave A untouched.
2348
void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)
2349
0
{
2350
0
    ImVec2 gradient_extent = gradient_p1 - gradient_p0;
2351
0
    float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent);
2352
0
    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
2353
0
    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
2354
0
    const int col0_r = (int)(col0 >> IM_COL32_R_SHIFT) & 0xFF;
2355
0
    const int col0_g = (int)(col0 >> IM_COL32_G_SHIFT) & 0xFF;
2356
0
    const int col0_b = (int)(col0 >> IM_COL32_B_SHIFT) & 0xFF;
2357
0
    const int col_delta_r = ((int)(col1 >> IM_COL32_R_SHIFT) & 0xFF) - col0_r;
2358
0
    const int col_delta_g = ((int)(col1 >> IM_COL32_G_SHIFT) & 0xFF) - col0_g;
2359
0
    const int col_delta_b = ((int)(col1 >> IM_COL32_B_SHIFT) & 0xFF) - col0_b;
2360
0
    for (ImDrawVert* vert = vert_start; vert < vert_end; vert++)
2361
0
    {
2362
0
        float d = ImDot(vert->pos - gradient_p0, gradient_extent);
2363
0
        float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f);
2364
0
        int r = (int)(col0_r + col_delta_r * t);
2365
0
        int g = (int)(col0_g + col_delta_g * t);
2366
0
        int b = (int)(col0_b + col_delta_b * t);
2367
0
        vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK);
2368
0
    }
2369
0
}
2370
2371
// Distribute UV over (a, b) rectangle
2372
void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp)
2373
0
{
2374
0
    const ImVec2 size = b - a;
2375
0
    const ImVec2 uv_size = uv_b - uv_a;
2376
0
    const ImVec2 scale = ImVec2(
2377
0
        size.x != 0.0f ? (uv_size.x / size.x) : 0.0f,
2378
0
        size.y != 0.0f ? (uv_size.y / size.y) : 0.0f);
2379
2380
0
    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
2381
0
    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
2382
0
    if (clamp)
2383
0
    {
2384
0
        const ImVec2 min = ImMin(uv_a, uv_b);
2385
0
        const ImVec2 max = ImMax(uv_a, uv_b);
2386
0
        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
2387
0
            vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max);
2388
0
    }
2389
0
    else
2390
0
    {
2391
0
        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
2392
0
            vertex->uv = uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale);
2393
0
    }
2394
0
}
2395
2396
void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)
2397
0
{
2398
0
    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
2399
0
    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
2400
0
    for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
2401
0
        vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;
2402
0
}
2403
2404
//-----------------------------------------------------------------------------
2405
// [SECTION] ImFontConfig
2406
//-----------------------------------------------------------------------------
2407
2408
// FIXME-NEWATLAS: Oversample specification could be more dynamic. For now, favoring automatic selection.
2409
ImFontConfig::ImFontConfig()
2410
1
{
2411
1
    memset(this, 0, sizeof(*this));
2412
1
    FontDataOwnedByAtlas = true;
2413
1
    OversampleH = 0; // Auto == 1 or 2 depending on size
2414
1
    OversampleV = 0; // Auto == 1
2415
1
    GlyphMaxAdvanceX = FLT_MAX;
2416
1
    RasterizerMultiply = 1.0f;
2417
1
    RasterizerDensity = 1.0f;
2418
1
    EllipsisChar = 0;
2419
1
}
2420
2421
//-----------------------------------------------------------------------------
2422
// [SECTION] ImTextureData
2423
//-----------------------------------------------------------------------------
2424
// - ImTextureData::Create()
2425
// - ImTextureData::DestroyPixels()
2426
//-----------------------------------------------------------------------------
2427
2428
int ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format)
2429
1
{
2430
1
    switch (format)
2431
1
    {
2432
0
    case ImTextureFormat_Alpha8: return 1;
2433
1
    case ImTextureFormat_RGBA32: return 4;
2434
1
    }
2435
0
    IM_ASSERT(0);
2436
0
    return 0;
2437
0
}
2438
2439
const char* ImTextureDataGetStatusName(ImTextureStatus status)
2440
0
{
2441
0
    switch (status)
2442
0
    {
2443
0
    case ImTextureStatus_OK: return "OK";
2444
0
    case ImTextureStatus_Destroyed: return "Destroyed";
2445
0
    case ImTextureStatus_WantCreate: return "WantCreate";
2446
0
    case ImTextureStatus_WantUpdates: return "WantUpdates";
2447
0
    case ImTextureStatus_WantDestroy: return "WantDestroy";
2448
0
    }
2449
0
    return "N/A";
2450
0
}
2451
2452
const char* ImTextureDataGetFormatName(ImTextureFormat format)
2453
0
{
2454
0
    switch (format)
2455
0
    {
2456
0
    case ImTextureFormat_Alpha8: return "Alpha8";
2457
0
    case ImTextureFormat_RGBA32: return "RGBA32";
2458
0
    }
2459
0
    return "N/A";
2460
0
}
2461
2462
void ImTextureData::Create(ImTextureFormat format, int w, int h)
2463
1
{
2464
1
    IM_ASSERT(Status == ImTextureStatus_Destroyed);
2465
1
    DestroyPixels();
2466
1
    Format = format;
2467
1
    Status = ImTextureStatus_WantCreate;
2468
1
    Width = w;
2469
1
    Height = h;
2470
1
    BytesPerPixel = ImTextureDataGetFormatBytesPerPixel(format);
2471
1
    UseColors = false;
2472
1
    Pixels = (unsigned char*)IM_ALLOC(Width * Height * BytesPerPixel);
2473
1
    IM_ASSERT(Pixels != NULL);
2474
1
    memset(Pixels, 0, Width * Height * BytesPerPixel);
2475
1
    UsedRect.x = UsedRect.y = UsedRect.w = UsedRect.h = 0;
2476
1
    UpdateRect.x = UpdateRect.y = (unsigned short)~0;
2477
1
    UpdateRect.w = UpdateRect.h = 0;
2478
1
}
2479
2480
void ImTextureData::DestroyPixels()
2481
3
{
2482
3
    if (Pixels)
2483
1
        IM_FREE(Pixels);
2484
3
    Pixels = NULL;
2485
3
    UseColors = false;
2486
3
}
2487
2488
//-----------------------------------------------------------------------------
2489
// [SECTION] ImFontAtlas, ImFontAtlasBuilder
2490
//-----------------------------------------------------------------------------
2491
// - Default texture data encoded in ASCII
2492
// - ImFontAtlas()
2493
// - ImFontAtlas::Clear()
2494
// - ImFontAtlas::CompactCache()
2495
// - ImFontAtlas::ClearInputData()
2496
// - ImFontAtlas::ClearTexData()
2497
// - ImFontAtlas::ClearFonts()
2498
//-----------------------------------------------------------------------------
2499
// - ImFontAtlasUpdateNewFrame()
2500
// - ImFontAtlasTextureBlockConvert()
2501
// - ImFontAtlasTextureBlockPostProcess()
2502
// - ImFontAtlasTextureBlockPostProcessMultiply()
2503
// - ImFontAtlasTextureBlockFill()
2504
// - ImFontAtlasTextureBlockCopy()
2505
// - ImFontAtlasTextureBlockQueueUpload()
2506
//-----------------------------------------------------------------------------
2507
// - ImFontAtlas::GetTexDataAsAlpha8() [legacy]
2508
// - ImFontAtlas::GetTexDataAsRGBA32() [legacy]
2509
// - ImFontAtlas::Build() [legacy]
2510
//-----------------------------------------------------------------------------
2511
// - ImFontAtlas::AddFont()
2512
// - ImFontAtlas::AddFontDefault()
2513
// - ImFontAtlas::AddFontFromFileTTF()
2514
// - ImFontAtlas::AddFontFromMemoryTTF()
2515
// - ImFontAtlas::AddFontFromMemoryCompressedTTF()
2516
// - ImFontAtlas::AddFontFromMemoryCompressedBase85TTF()
2517
// - ImFontAtlas::RemoveFont()
2518
// - ImFontAtlasBuildNotifySetFont()
2519
//-----------------------------------------------------------------------------
2520
// - ImFontAtlas::AddCustomRect()
2521
// - ImFontAtlas::RemoveCustomRect()
2522
// - ImFontAtlas::GetCustomRect()
2523
// - ImFontAtlas::AddCustomRectFontGlyph() [legacy]
2524
// - ImFontAtlas::AddCustomRectFontGlyphForSize() [legacy]
2525
// - ImFontAtlasGetMouseCursorTexData()
2526
//-----------------------------------------------------------------------------
2527
// - ImFontAtlasBuildMain()
2528
// - ImFontAtlasBuildSetupFontLoader()
2529
// - ImFontAtlasBuildPreloadAllGlyphRanges()
2530
// - ImFontAtlasBuildUpdatePointers()
2531
// - ImFontAtlasBuildRenderBitmapFromString()
2532
// - ImFontAtlasBuildUpdateBasicTexData()
2533
// - ImFontAtlasBuildUpdateLinesTexData()
2534
// - ImFontAtlasBuildAddFont()
2535
// - ImFontAtlasBuildSetupFontBakedEllipsis()
2536
// - ImFontAtlasBuildSetupFontBakedBlanks()
2537
// - ImFontAtlasBuildSetupFontBakedFallback()
2538
// - ImFontAtlasBuildSetupFontSpecialGlyphs()
2539
// - ImFontAtlasBuildDiscardBakes()
2540
// - ImFontAtlasBuildDiscardFontBakedGlyph()
2541
// - ImFontAtlasBuildDiscardFontBaked()
2542
// - ImFontAtlasBuildDiscardFontBakes()
2543
//-----------------------------------------------------------------------------
2544
// - ImFontAtlasAddDrawListSharedData()
2545
// - ImFontAtlasRemoveDrawListSharedData()
2546
// - ImFontAtlasUpdateDrawListsTextures()
2547
// - ImFontAtlasUpdateDrawListsSharedData()
2548
//-----------------------------------------------------------------------------
2549
// - ImFontAtlasBuildSetTexture()
2550
// - ImFontAtlasBuildAddTexture()
2551
// - ImFontAtlasBuildMakeSpace()
2552
// - ImFontAtlasBuildRepackTexture()
2553
// - ImFontAtlasBuildGrowTexture()
2554
// - ImFontAtlasBuildRepackOrGrowTexture()
2555
// - ImFontAtlasBuildGetTextureSizeEstimate()
2556
// - ImFontAtlasBuildCompactTexture()
2557
// - ImFontAtlasBuildInit()
2558
// - ImFontAtlasBuildDestroy()
2559
//-----------------------------------------------------------------------------
2560
// - ImFontAtlasPackInit()
2561
// - ImFontAtlasPackAllocRectEntry()
2562
// - ImFontAtlasPackReuseRectEntry()
2563
// - ImFontAtlasPackDiscardRect()
2564
// - ImFontAtlasPackAddRect()
2565
// - ImFontAtlasPackGetRect()
2566
//-----------------------------------------------------------------------------
2567
// - ImFontBaked_BuildGrowIndex()
2568
// - ImFontBaked_BuildLoadGlyph()
2569
// - ImFontBaked_BuildLoadGlyphAdvanceX()
2570
// - ImFontAtlasDebugLogTextureRequests()
2571
//-----------------------------------------------------------------------------
2572
// - ImFontAtlasGetFontLoaderForStbTruetype()
2573
//-----------------------------------------------------------------------------
2574
2575
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
2576
// The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.
2577
// (This is used when io.MouseDrawCursor = true)
2578
const int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing.
2579
const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
2580
static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
2581
{
2582
    "..-         -XXXXXXX-    X    -           X           -XXXXXXX          -          XXXXXXX-     XX          - XX       XX "
2583
    "..-         -X.....X-   X.X   -          X.X          -X.....X          -          X.....X-    X..X         -X..X     X..X"
2584
    "---         -XXX.XXX-  X...X  -         X...X         -X....X           -           X....X-    X..X         -X...X   X...X"
2585
    "X           -  X.X  - X.....X -        X.....X        -X...X            -            X...X-    X..X         - X...X X...X "
2586
    "XX          -  X.X  -X.......X-       X.......X       -X..X.X           -           X.X..X-    X..X         -  X...X...X  "
2587
    "X.X         -  X.X  -XXXX.XXXX-       XXXX.XXXX       -X.X X.X          -          X.X X.X-    X..XXX       -   X.....X   "
2588
    "X..X        -  X.X  -   X.X   -          X.X          -XX   X.X         -         X.X   XX-    X..X..XXX    -    X...X    "
2589
    "X...X       -  X.X  -   X.X   -    XX    X.X    XX    -      X.X        -        X.X      -    X..X..X..XX  -     X.X     "
2590
    "X....X      -  X.X  -   X.X   -   X.X    X.X    X.X   -       X.X       -       X.X       -    X..X..X..X.X -    X...X    "
2591
    "X.....X     -  X.X  -   X.X   -  X..X    X.X    X..X  -        X.X      -      X.X        -XXX X..X..X..X..X-   X.....X   "
2592
    "X......X    -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -         X.X   XX-XX   X.X         -X..XX........X..X-  X...X...X  "
2593
    "X.......X   -  X.X  -   X.X   -X.....................X-          X.X X.X-X.X X.X          -X...X...........X- X...X X...X "
2594
    "X........X  -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -           X.X..X-X..X.X           - X..............X-X...X   X...X"
2595
    "X.........X -XXX.XXX-   X.X   -  X..X    X.X    X..X  -            X...X-X...X            -  X.............X-X..X     X..X"
2596
    "X..........X-X.....X-   X.X   -   X.X    X.X    X.X   -           X....X-X....X           -  X.............X- XX       XX "
2597
    "X......XXXXX-XXXXXXX-   X.X   -    XX    X.X    XX    -          X.....X-X.....X          -   X............X--------------"
2598
    "X...X..X    ---------   X.X   -          X.X          -          XXXXXXX-XXXXXXX          -   X...........X -             "
2599
    "X..X X..X   -       -XXXX.XXXX-       XXXX.XXXX       -------------------------------------    X..........X -             "
2600
    "X.X  X..X   -       -X.......X-       X.......X       -    XX           XX    -           -    X..........X -             "
2601
    "XX    X..X  -       - X.....X -        X.....X        -   X.X           X.X   -           -     X........X  -             "
2602
    "      X..X  -       -  X...X  -         X...X         -  X..X           X..X  -           -     X........X  -             "
2603
    "       XX   -       -   X.X   -          X.X          - X...XXXXXXXXXXXXX...X -           -     XXXXXXXXXX  -             "
2604
    "-------------       -    X    -           X           -X.....................X-           -------------------             "
2605
    "                    ----------------------------------- X...XXXXXXXXXXXXX...X -                                           "
2606
    "                                                      -  X..X           X..X  -                                           "
2607
    "                                                      -   X.X           X.X   -                                           "
2608
    "                                                      -    XX           XX    -                                           "
2609
};
2610
2611
static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] =
2612
{
2613
    // Pos ........ Size ......... Offset ......
2614
    { ImVec2( 0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow
2615
    { ImVec2(13,0), ImVec2( 7,16), ImVec2( 1, 8) }, // ImGuiMouseCursor_TextInput
2616
    { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll
2617
    { ImVec2(21,0), ImVec2( 9,23), ImVec2( 4,11) }, // ImGuiMouseCursor_ResizeNS
2618
    { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 4) }, // ImGuiMouseCursor_ResizeEW
2619
    { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW
2620
    { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE
2621
    { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand
2622
    { ImVec2(0,3),  ImVec2(12,19), ImVec2(0, 0) },  // ImGuiMouseCursor_Wait       // Arrow + custom code in ImGui::RenderMouseCursor()
2623
    { ImVec2(0,3),  ImVec2(12,19), ImVec2(0, 0) },  // ImGuiMouseCursor_Progress   // Arrow + custom code in ImGui::RenderMouseCursor()
2624
    { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed
2625
};
2626
2627
1.54M
#define IM_FONTGLYPH_INDEX_UNUSED           ((ImU16)-1) // 0xFFFF
2628
1.54M
#define IM_FONTGLYPH_INDEX_NOT_FOUND        ((ImU16)-2) // 0xFFFE
2629
2630
ImFontAtlas::ImFontAtlas()
2631
1
{
2632
1
    memset(this, 0, sizeof(*this));
2633
1
    TexDesiredFormat = ImTextureFormat_RGBA32;
2634
1
    TexGlyphPadding = 1;
2635
1
    TexMinWidth = 512;
2636
1
    TexMinHeight = 128;
2637
1
    TexMaxWidth = 8192;
2638
1
    TexMaxHeight = 8192;
2639
1
    TexRef._TexID = ImTextureID_Invalid;
2640
1
    RendererHasTextures = false; // Assumed false by default, as apps can call e.g Atlas::Build() after backend init and before ImGui can update.
2641
1
    TexNextUniqueID = 1;
2642
1
    FontNextUniqueID = 1;
2643
1
    Builder = NULL;
2644
1
}
2645
2646
ImFontAtlas::~ImFontAtlas()
2647
1
{
2648
1
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
2649
1
    RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.
2650
1
    ClearFonts();
2651
1
    ClearTexData();
2652
1
    TexList.clear_delete();
2653
1
    TexData = NULL;
2654
1
}
2655
2656
void ImFontAtlas::Clear()
2657
0
{
2658
0
    bool backup_renderer_has_textures = RendererHasTextures;
2659
0
    RendererHasTextures = false; // Full Clear() is supported, but ClearTexData() only isn't.
2660
0
    ClearFonts();
2661
0
    ClearTexData();
2662
0
    RendererHasTextures = backup_renderer_has_textures;
2663
0
}
2664
2665
void ImFontAtlas::CompactCache()
2666
0
{
2667
0
    ImFontAtlasTextureCompact(this);
2668
0
}
2669
2670
void ImFontAtlas::SetFontLoader(const ImFontLoader* font_loader)
2671
1
{
2672
1
    ImFontAtlasBuildSetupFontLoader(this, font_loader);
2673
1
}
2674
2675
void ImFontAtlas::ClearInputData()
2676
1
{
2677
1
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
2678
2679
1
    for (ImFont* font : Fonts)
2680
1
        ImFontAtlasFontDestroyOutput(this, font);
2681
1
    for (ImFontConfig& font_cfg : Sources)
2682
1
        ImFontAtlasFontDestroySourceData(this, &font_cfg);
2683
1
    for (ImFont* font : Fonts)
2684
1
    {
2685
        // When clearing this we lose access to the font name and other information used to build the font.
2686
1
        font->Sources.clear();
2687
1
        font->Flags |= ImFontFlags_NoLoadGlyphs;
2688
1
    }
2689
1
    Sources.clear();
2690
1
}
2691
2692
// Clear CPU-side copy of the texture data.
2693
void ImFontAtlas::ClearTexData()
2694
1
{
2695
1
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
2696
1
    IM_ASSERT(RendererHasTextures == false && "Not supported for dynamic atlases, but you may call Clear().");
2697
1
    for (ImTextureData* tex : TexList)
2698
1
        tex->DestroyPixels();
2699
    //Locked = true; // Hoped to be able to lock this down but some reload patterns may not be happy with it.
2700
1
}
2701
2702
void ImFontAtlas::ClearFonts()
2703
1
{
2704
    // FIXME-NEWATLAS: Illegal to remove currently bound font.
2705
1
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
2706
1
    ImFontAtlasBuildDestroy(this);
2707
1
    ClearInputData();
2708
1
    Fonts.clear_delete();
2709
1
    TexIsBuilt = false;
2710
1
    for (ImDrawListSharedData* shared_data : DrawListSharedDatas)
2711
0
        if (shared_data->FontAtlas == this)
2712
0
        {
2713
0
            shared_data->Font = NULL;
2714
0
            shared_data->FontScale = shared_data->FontSize = 0.0f;
2715
0
        }
2716
1
}
2717
2718
static void ImFontAtlasBuildUpdateRendererHasTexturesFromContext(ImFontAtlas* atlas)
2719
2
{
2720
    // [LEGACY] Copy back the ImGuiBackendFlags_RendererHasTextures flag from ImGui context.
2721
    // - This is the 1% exceptional case where that dependency if useful, to bypass an issue where otherwise at the
2722
    //   time of an early call to Build(), it would be impossible for us to tell if the backend supports texture update.
2723
    // - Without this hack, we would have quite a pitfall as many legacy codebases have an early call to Build().
2724
    //   Whereas conversely, the portion of people using ImDrawList without ImGui is expected to be pathologically rare.
2725
2
    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
2726
2
        if (ImGuiContext* imgui_ctx = shared_data->Context)
2727
2
        {
2728
2
            atlas->RendererHasTextures = (imgui_ctx->IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) != 0;
2729
2
            break;
2730
2
        }
2731
2
}
2732
2733
// Called by NewFrame() for atlases owned by a context.
2734
// If you manually manage font atlases, you'll need to call this yourself.
2735
// - 'frame_count' needs to be provided because we can gc/prioritize baked fonts based on their age.
2736
// - 'frame_count' may not match those of all imgui contexts using this atlas, as contexts may be updated as different frequencies. But generally you can use ImGui::GetFrameCount() on one of your context.
2737
void ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures)
2738
80.5k
{
2739
80.5k
    IM_ASSERT(atlas->Builder == NULL || atlas->Builder->FrameCount < frame_count); // Protection against being called twice.
2740
80.5k
    atlas->RendererHasTextures = renderer_has_textures;
2741
2742
    // Check that font atlas was built or backend support texture reload in which case we can build now
2743
80.5k
    if (atlas->RendererHasTextures)
2744
80.5k
    {
2745
80.5k
        atlas->TexIsBuilt = true;
2746
80.5k
        if (atlas->Builder == NULL) // This will only happen if fonts were not already loaded.
2747
1
            ImFontAtlasBuildMain(atlas);
2748
80.5k
    }
2749
    // Legacy backend
2750
80.5k
    if (!atlas->RendererHasTextures)
2751
0
        IM_ASSERT_USER_ERROR(atlas->TexIsBuilt, "Backend does not support ImGuiBackendFlags_RendererHasTextures, and font atlas is not built! Update backend OR make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8().");
2752
80.5k
    if (atlas->TexIsBuilt && atlas->Builder->PreloadedAllGlyphsRanges)
2753
0
        IM_ASSERT_USER_ERROR(atlas->RendererHasTextures == false, "Called ImFontAtlas::Build() before ImGuiBackendFlags_RendererHasTextures got set! With new backends: you don't need to call Build().");
2754
2755
    // Clear BakedCurrent cache, this is important because it ensure the uncached path gets taken once.
2756
    // We also rely on ImFontBaked* pointers never crossing frames.
2757
80.5k
    ImFontAtlasBuilder* builder = atlas->Builder;
2758
80.5k
    builder->FrameCount = frame_count;
2759
80.5k
    for (ImFont* font : atlas->Fonts)
2760
80.5k
        font->LastBaked = NULL;
2761
2762
    // Garbage collect BakedPool
2763
80.5k
    if (builder->BakedDiscardedCount > 0)
2764
0
    {
2765
0
        int dst_n = 0, src_n = 0;
2766
0
        for (; src_n < builder->BakedPool.Size; src_n++)
2767
0
        {
2768
0
            ImFontBaked* p_src = &builder->BakedPool[src_n];
2769
0
            if (p_src->WantDestroy)
2770
0
                continue;
2771
0
            ImFontBaked* p_dst = &builder->BakedPool[dst_n++];
2772
0
            if (p_dst == p_src)
2773
0
                continue;
2774
0
            memcpy(p_dst, p_src, sizeof(ImFontBaked));
2775
0
            builder->BakedMap.SetVoidPtr(p_dst->BakedId, p_dst);
2776
0
        }
2777
0
        IM_ASSERT(dst_n + builder->BakedDiscardedCount == src_n);
2778
0
        builder->BakedPool.Size -= builder->BakedDiscardedCount;
2779
0
        builder->BakedDiscardedCount = 0;
2780
0
    }
2781
2782
    // Update texture status
2783
161k
    for (int tex_n = 0; tex_n < atlas->TexList.Size; tex_n++)
2784
80.5k
    {
2785
80.5k
        ImTextureData* tex = atlas->TexList[tex_n];
2786
80.5k
        bool remove_from_list = false;
2787
80.5k
        if (tex->Status == ImTextureStatus_OK)
2788
80.5k
        {
2789
80.5k
            tex->Updates.resize(0);
2790
80.5k
            tex->UpdateRect.x = tex->UpdateRect.y = (unsigned short)~0;
2791
80.5k
            tex->UpdateRect.w = tex->UpdateRect.h = 0;
2792
80.5k
        }
2793
80.5k
        if (tex->Status == ImTextureStatus_WantCreate && atlas->RendererHasTextures)
2794
80.5k
            IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture's TexID/BackendUserData but did not update Status to OK.");
2795
2796
        // Request destroy
2797
        // - Keep bool to true in order to differentiate a planned destroy vs a destroy decided by the backend.
2798
        // - We don't destroy pixels right away, as backend may have an in-flight copy from RAM.
2799
80.5k
        if (tex->WantDestroyNextFrame && tex->Status != ImTextureStatus_Destroyed && tex->Status != ImTextureStatus_WantDestroy)
2800
0
        {
2801
0
            IM_ASSERT(tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates);
2802
0
            tex->Status = ImTextureStatus_WantDestroy;
2803
0
        }
2804
2805
        // If a texture has never reached the backend, they don't need to know about it.
2806
        // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy
2807
        //  when invalidating graphics objects twice, which would previously remove it from the list and crash.)
2808
80.5k
        if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL)
2809
0
            tex->Status = ImTextureStatus_Destroyed;
2810
2811
        // Process texture being destroyed
2812
80.5k
        if (tex->Status == ImTextureStatus_Destroyed)
2813
0
        {
2814
0
            IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!");
2815
0
            if (tex->WantDestroyNextFrame)
2816
0
                remove_from_list = true; // Destroy was scheduled by us
2817
0
            else
2818
0
                tex->Status = ImTextureStatus_WantCreate; // Destroy was done was backend: recreate it (e.g. freed resources mid-run)
2819
0
        }
2820
2821
        // The backend may need defer destroying by a few frames, to handle texture used by previous in-flight rendering.
2822
        // We allow the texture staying in _WantDestroy state and increment a counter which the backend can use to take its decision.
2823
80.5k
        if (tex->Status == ImTextureStatus_WantDestroy)
2824
0
            tex->UnusedFrames++;
2825
2826
        // Destroy and remove
2827
80.5k
        if (remove_from_list)
2828
0
        {
2829
0
            IM_ASSERT(atlas->TexData != tex);
2830
0
            tex->DestroyPixels();
2831
0
            IM_DELETE(tex);
2832
0
            atlas->TexList.erase(atlas->TexList.begin() + tex_n);
2833
0
            tex_n--;
2834
0
        }
2835
80.5k
    }
2836
80.5k
}
2837
2838
void ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h)
2839
20
{
2840
20
    IM_ASSERT(src_pixels != NULL && dst_pixels != NULL);
2841
20
    if (src_fmt == dst_fmt)
2842
0
    {
2843
0
        int line_sz = w * ImTextureDataGetFormatBytesPerPixel(src_fmt);
2844
0
        for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)
2845
0
            memcpy(dst_pixels, src_pixels, line_sz);
2846
0
    }
2847
20
    else if (src_fmt == ImTextureFormat_Alpha8 && dst_fmt == ImTextureFormat_RGBA32)
2848
20
    {
2849
173
        for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)
2850
153
        {
2851
153
            const ImU8* src_p = (const ImU8*)src_pixels;
2852
153
            ImU32* dst_p = (ImU32*)(void*)dst_pixels;
2853
916
            for (int nx = w; nx > 0; nx--)
2854
763
                *dst_p++ = IM_COL32(255, 255, 255, (unsigned int)(*src_p++));
2855
153
        }
2856
20
    }
2857
0
    else if (src_fmt == ImTextureFormat_RGBA32 && dst_fmt == ImTextureFormat_Alpha8)
2858
0
    {
2859
0
        for (int ny = h; ny > 0; ny--, src_pixels += src_pitch, dst_pixels += dst_pitch)
2860
0
        {
2861
0
            const ImU32* src_p = (const ImU32*)(void*)src_pixels;
2862
0
            ImU8* dst_p = (ImU8*)dst_pixels;
2863
0
            for (int nx = w; nx > 0; nx--)
2864
0
                *dst_p++ = ((*src_p++) >> IM_COL32_A_SHIFT) & 0xFF;
2865
0
        }
2866
0
    }
2867
0
    else
2868
0
    {
2869
0
        IM_ASSERT(0);
2870
0
    }
2871
20
}
2872
2873
// Source buffer may be written to (used for in-place mods).
2874
// Post-process hooks may eventually be added here.
2875
void ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data)
2876
20
{
2877
    // Multiply operator (legacy)
2878
20
    if (data->FontSrc->RasterizerMultiply != 1.0f)
2879
0
        ImFontAtlasTextureBlockPostProcessMultiply(data, data->FontSrc->RasterizerMultiply);
2880
20
}
2881
2882
void ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor)
2883
0
{
2884
0
    unsigned char* pixels = (unsigned char*)data->Pixels;
2885
0
    int pitch = data->Pitch;
2886
0
    if (data->Format == ImTextureFormat_Alpha8)
2887
0
    {
2888
0
        for (int ny = data->Height; ny > 0; ny--, pixels += pitch)
2889
0
        {
2890
0
            ImU8* p = (ImU8*)pixels;
2891
0
            for (int nx = data->Width; nx > 0; nx--, p++)
2892
0
            {
2893
0
                unsigned int v = ImMin((unsigned int)(*p * multiply_factor), (unsigned int)255);
2894
0
                *p = (unsigned char)v;
2895
0
            }
2896
0
        }
2897
0
    }
2898
0
    else if (data->Format == ImTextureFormat_RGBA32) //-V547
2899
0
    {
2900
0
        for (int ny = data->Height; ny > 0; ny--, pixels += pitch)
2901
0
        {
2902
0
            ImU32* p = (ImU32*)(void*)pixels;
2903
0
            for (int nx = data->Width; nx > 0; nx--, p++)
2904
0
            {
2905
0
                unsigned int a = ImMin((unsigned int)(((*p >> IM_COL32_A_SHIFT) & 0xFF) * multiply_factor), (unsigned int)255);
2906
0
                *p = IM_COL32((*p >> IM_COL32_R_SHIFT) & 0xFF, (*p >> IM_COL32_G_SHIFT) & 0xFF, (*p >> IM_COL32_B_SHIFT) & 0xFF, a);
2907
0
            }
2908
0
        }
2909
0
    }
2910
0
    else
2911
0
    {
2912
0
        IM_ASSERT(0);
2913
0
    }
2914
0
}
2915
2916
// Fill with single color. We don't use this directly but it is convenient for anyone working on uploading custom rects.
2917
void ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col)
2918
0
{
2919
0
    if (dst_tex->Format == ImTextureFormat_Alpha8)
2920
0
    {
2921
0
        ImU8 col_a = (col >> IM_COL32_A_SHIFT) & 0xFF;
2922
0
        for (int y = 0; y < h; y++)
2923
0
            memset((ImU8*)dst_tex->GetPixelsAt(dst_x, dst_y + y), col_a, w);
2924
0
    }
2925
0
    else
2926
0
    {
2927
0
        for (int y = 0; y < h; y++)
2928
0
        {
2929
0
            ImU32* p = (ImU32*)(void*)dst_tex->GetPixelsAt(dst_x, dst_y + y);
2930
0
            for (int x = w; x > 0; x--, p++)
2931
0
                *p = col;
2932
0
        }
2933
0
    }
2934
0
}
2935
2936
// Copy block from one texture to another
2937
void ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h)
2938
0
{
2939
0
    IM_ASSERT(src_tex->Pixels != NULL && dst_tex->Pixels != NULL);
2940
0
    IM_ASSERT(src_tex->Format == dst_tex->Format);
2941
0
    IM_ASSERT(src_x >= 0 && src_x + w <= src_tex->Width);
2942
0
    IM_ASSERT(src_y >= 0 && src_y + h <= src_tex->Height);
2943
0
    IM_ASSERT(dst_x >= 0 && dst_x + w <= dst_tex->Width);
2944
0
    IM_ASSERT(dst_y >= 0 && dst_y + h <= dst_tex->Height);
2945
0
    for (int y = 0; y < h; y++)
2946
0
        memcpy(dst_tex->GetPixelsAt(dst_x, dst_y + y), src_tex->GetPixelsAt(src_x, src_y + y), w * dst_tex->BytesPerPixel);
2947
0
}
2948
2949
// Queue texture block update for renderer backend
2950
void ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h)
2951
22
{
2952
22
    IM_ASSERT(tex->Status != ImTextureStatus_WantDestroy && tex->Status != ImTextureStatus_Destroyed);
2953
22
    IM_ASSERT(x >= 0 && x <= 0xFFFF && y >= 0 && y <= 0xFFFF && w >= 0 && x + w <= 0x10000 && h >= 0 && y + h <= 0x10000);
2954
22
    IM_UNUSED(atlas);
2955
2956
22
    ImTextureRect req = { (unsigned short)x, (unsigned short)y, (unsigned short)w, (unsigned short)h };
2957
22
    int new_x1 = ImMax(tex->UpdateRect.w == 0 ? 0 : tex->UpdateRect.x + tex->UpdateRect.w, req.x + req.w);
2958
22
    int new_y1 = ImMax(tex->UpdateRect.h == 0 ? 0 : tex->UpdateRect.y + tex->UpdateRect.h, req.y + req.h);
2959
22
    tex->UpdateRect.x = ImMin(tex->UpdateRect.x, req.x);
2960
22
    tex->UpdateRect.y = ImMin(tex->UpdateRect.y, req.y);
2961
22
    tex->UpdateRect.w = (unsigned short)(new_x1 - tex->UpdateRect.x);
2962
22
    tex->UpdateRect.h = (unsigned short)(new_y1 - tex->UpdateRect.y);
2963
22
    tex->UsedRect.x = ImMin(tex->UsedRect.x, req.x);
2964
22
    tex->UsedRect.y = ImMin(tex->UsedRect.y, req.y);
2965
22
    tex->UsedRect.w = (unsigned short)(ImMax(tex->UsedRect.x + tex->UsedRect.w, req.x + req.w) - tex->UsedRect.x);
2966
22
    tex->UsedRect.h = (unsigned short)(ImMax(tex->UsedRect.y + tex->UsedRect.h, req.y + req.h) - tex->UsedRect.y);
2967
22
    atlas->TexIsBuilt = false;
2968
2969
    // No need to queue if status is == ImTextureStatus_WantCreate
2970
22
    if (tex->Status == ImTextureStatus_OK || tex->Status == ImTextureStatus_WantUpdates)
2971
4
    {
2972
4
        tex->Status = ImTextureStatus_WantUpdates;
2973
4
        tex->Updates.push_back(req);
2974
4
    }
2975
22
}
2976
2977
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2978
static void GetTexDataAsFormat(ImFontAtlas* atlas, ImTextureFormat format, unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
2979
0
{
2980
0
    ImTextureData* tex = atlas->TexData;
2981
0
    if (!atlas->TexIsBuilt || tex == NULL || tex->Pixels == NULL || atlas->TexDesiredFormat != format)
2982
0
    {
2983
0
        atlas->TexDesiredFormat = format;
2984
0
        atlas->Build();
2985
0
        tex = atlas->TexData;
2986
0
    }
2987
0
    if (out_pixels) { *out_pixels = (unsigned char*)tex->Pixels; };
2988
0
    if (out_width) { *out_width = tex->Width; };
2989
0
    if (out_height) { *out_height = tex->Height; };
2990
0
    if (out_bytes_per_pixel) { *out_bytes_per_pixel = tex->BytesPerPixel; }
2991
0
}
2992
2993
void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
2994
0
{
2995
0
    GetTexDataAsFormat(this, ImTextureFormat_Alpha8, out_pixels, out_width, out_height, out_bytes_per_pixel);
2996
0
}
2997
2998
void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
2999
0
{
3000
0
    GetTexDataAsFormat(this, ImTextureFormat_RGBA32, out_pixels, out_width, out_height, out_bytes_per_pixel);
3001
0
}
3002
3003
bool ImFontAtlas::Build()
3004
0
{
3005
0
    ImFontAtlasBuildMain(this);
3006
0
    return true;
3007
0
}
3008
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3009
3010
ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in)
3011
1
{
3012
    // Sanity Checks
3013
1
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
3014
1
    IM_ASSERT((font_cfg_in->FontData != NULL && font_cfg_in->FontDataSize > 0) || (font_cfg_in->FontLoader != NULL));
3015
    //IM_ASSERT(font_cfg_in->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?");
3016
1
    IM_ASSERT(font_cfg_in->RasterizerDensity > 0.0f && "Is ImFontConfig struct correctly initialized?");
3017
1
    if (font_cfg_in->GlyphOffset.x != 0.0f || font_cfg_in->GlyphOffset.y != 0.0f || font_cfg_in->GlyphMinAdvanceX != 0.0f || font_cfg_in->GlyphMaxAdvanceX != FLT_MAX)
3018
1
        IM_ASSERT(font_cfg_in->SizePixels != 0.0f && "Specifying glyph offset/advances requires a reference size to base it on.");
3019
3020
    // Lazily create builder on the first call to AddFont
3021
1
    if (Builder == NULL)
3022
0
        ImFontAtlasBuildInit(this);
3023
3024
    // Create new font
3025
1
    ImFont* font;
3026
1
    if (!font_cfg_in->MergeMode)
3027
1
    {
3028
1
        font = IM_NEW(ImFont)();
3029
1
        font->FontId = FontNextUniqueID++;
3030
1
        font->Flags = font_cfg_in->Flags;
3031
1
        font->LegacySize = font_cfg_in->SizePixels;
3032
1
        font->CurrentRasterizerDensity = font_cfg_in->RasterizerDensity;
3033
1
        Fonts.push_back(font);
3034
1
    }
3035
0
    else
3036
0
    {
3037
0
        IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
3038
0
        font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back();
3039
0
    }
3040
3041
    // Add to list
3042
1
    Sources.push_back(*font_cfg_in);
3043
1
    ImFontConfig* font_cfg = &Sources.back();
3044
1
    if (font_cfg->DstFont == NULL)
3045
1
        font_cfg->DstFont = font;
3046
1
    font->Sources.push_back(font_cfg);
3047
1
    ImFontAtlasBuildUpdatePointers(this); // Pointers to Sources are otherwise dangling after we called Sources.push_back().
3048
3049
1
    if (font_cfg->FontDataOwnedByAtlas == false)
3050
0
    {
3051
0
        font_cfg->FontDataOwnedByAtlas = true;
3052
0
        font_cfg->FontData = ImMemdup(font_cfg->FontData, (size_t)font_cfg->FontDataSize);
3053
0
    }
3054
3055
    // Sanity check
3056
    // We don't round cfg.SizePixels yet as relative size of merged fonts are used afterwards.
3057
1
    if (font_cfg->GlyphExcludeRanges != NULL)
3058
0
    {
3059
0
        int size = 0;
3060
0
        for (const ImWchar* p = font_cfg->GlyphExcludeRanges; p[0] != 0; p++, size++) {}
3061
0
        IM_ASSERT((size & 1) == 0 && "GlyphExcludeRanges[] size must be multiple of two!");
3062
0
        IM_ASSERT((size <= 64) && "GlyphExcludeRanges[] size must be small!");
3063
0
        font_cfg->GlyphExcludeRanges = (ImWchar*)ImMemdup(font_cfg->GlyphExcludeRanges, sizeof(font_cfg->GlyphExcludeRanges[0]) * (size + 1));
3064
0
    }
3065
1
    if (font_cfg->FontLoader != NULL)
3066
0
    {
3067
0
        IM_ASSERT(font_cfg->FontLoader->FontBakedLoadGlyph != NULL);
3068
0
        IM_ASSERT(font_cfg->FontLoader->LoaderInit == NULL && font_cfg->FontLoader->LoaderShutdown == NULL); // FIXME-NEWATLAS: Unsupported yet.
3069
0
    }
3070
1
    IM_ASSERT(font_cfg->FontLoaderData == NULL);
3071
3072
1
    if (!ImFontAtlasFontSourceInit(this, font_cfg))
3073
0
    {
3074
        // Rollback (this is a fragile/rarely exercised code-path. TestSuite's "misc_atlas_add_invalid_font" aim to test this)
3075
0
        ImFontAtlasFontDestroySourceData(this, font_cfg);
3076
0
        Sources.pop_back();
3077
0
        font->Sources.pop_back();
3078
0
        if (!font_cfg->MergeMode)
3079
0
        {
3080
0
            IM_DELETE(font);
3081
0
            Fonts.pop_back();
3082
0
        }
3083
0
        return NULL;
3084
0
    }
3085
1
    ImFontAtlasFontSourceAddToFont(this, font, font_cfg);
3086
3087
1
    return font;
3088
1
}
3089
3090
// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)
3091
static unsigned int stb_decompress_length(const unsigned char* input);
3092
static unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);
3093
0
static unsigned int Decode85Byte(char c)                                    { return c >= '\\' ? c-36 : c-35; }
3094
static void         Decode85(const unsigned char* src, unsigned char* dst)
3095
0
{
3096
0
    while (*src)
3097
0
    {
3098
0
        unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4]))));
3099
0
        dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF);   // We can't assume little-endianness.
3100
0
        src += 5;
3101
0
        dst += 4;
3102
0
    }
3103
0
}
3104
#ifndef IMGUI_DISABLE_DEFAULT_FONT
3105
static const char* GetDefaultCompressedFontDataTTF(int* out_size);
3106
#endif
3107
3108
// Load embedded ProggyClean.ttf at size 13, disable oversampling
3109
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
3110
ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
3111
1
{
3112
1
#ifndef IMGUI_DISABLE_DEFAULT_FONT
3113
1
    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
3114
1
    if (!font_cfg_template)
3115
1
    {
3116
1
        font_cfg.OversampleH = font_cfg.OversampleV = 1;
3117
1
        font_cfg.PixelSnapH = true;
3118
1
    }
3119
1
    if (font_cfg.SizePixels <= 0.0f)
3120
1
        font_cfg.SizePixels = 13.0f * 1.0f;
3121
1
    if (font_cfg.Name[0] == '\0')
3122
1
        ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf");
3123
1
    font_cfg.EllipsisChar = (ImWchar)0x0085;
3124
1
    font_cfg.GlyphOffset.y += 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f);  // Add +1 offset per 13 units
3125
3126
1
    int ttf_compressed_size = 0;
3127
1
    const char* ttf_compressed = GetDefaultCompressedFontDataTTF(&ttf_compressed_size);
3128
1
    return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg);
3129
#else
3130
    IM_ASSERT(0 && "AddFontDefault() disabled in this build.");
3131
    IM_UNUSED(font_cfg_template);
3132
    return NULL;
3133
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
3134
1
}
3135
3136
ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
3137
0
{
3138
0
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
3139
0
    size_t data_size = 0;
3140
0
    void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0);
3141
0
    if (!data)
3142
0
    {
3143
0
        if (font_cfg_template == NULL || (font_cfg_template->Flags & ImFontFlags_NoLoadError) == 0)
3144
0
        {
3145
0
            IMGUI_DEBUG_LOG("While loading '%s'\n", filename);
3146
0
            IM_ASSERT_USER_ERROR(0, "Could not load font file!");
3147
0
        }
3148
0
        return NULL;
3149
0
    }
3150
0
    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
3151
0
    if (font_cfg.Name[0] == '\0')
3152
0
    {
3153
        // Store a short copy of filename into into the font name for convenience
3154
0
        const char* p;
3155
0
        for (p = filename + ImStrlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
3156
0
        ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p);
3157
0
    }
3158
0
    return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);
3159
0
}
3160
3161
// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
3162
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
3163
1
{
3164
1
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
3165
1
    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
3166
1
    IM_ASSERT(font_cfg.FontData == NULL);
3167
1
    IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.
3168
1
    font_cfg.FontData = font_data;
3169
1
    font_cfg.FontDataSize = font_data_size;
3170
1
    font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;
3171
1
    if (glyph_ranges)
3172
0
        font_cfg.GlyphRanges = glyph_ranges;
3173
1
    return AddFont(&font_cfg);
3174
1
}
3175
3176
ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
3177
1
{
3178
1
    const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data);
3179
1
    unsigned char* buf_decompressed_data = (unsigned char*)IM_ALLOC(buf_decompressed_size);
3180
1
    stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size);
3181
3182
1
    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
3183
1
    IM_ASSERT(font_cfg.FontData == NULL);
3184
1
    font_cfg.FontDataOwnedByAtlas = true;
3185
1
    return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges);
3186
1
}
3187
3188
ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)
3189
0
{
3190
0
    int compressed_ttf_size = (((int)ImStrlen(compressed_ttf_data_base85) + 4) / 5) * 4;
3191
0
    void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size);
3192
0
    Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf);
3193
0
    ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);
3194
0
    IM_FREE(compressed_ttf);
3195
0
    return font;
3196
0
}
3197
3198
// On font removal we need to remove references (otherwise we could queue removal?)
3199
// We allow old_font == new_font which forces updating all values (e.g. sizes)
3200
static void ImFontAtlasBuildNotifySetFont(ImFontAtlas* atlas, ImFont* old_font, ImFont* new_font)
3201
0
{
3202
0
    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
3203
0
    {
3204
0
        if (shared_data->Font == old_font)
3205
0
            shared_data->Font = new_font;
3206
0
        if (ImGuiContext* ctx = shared_data->Context)
3207
0
        {
3208
0
            if (ctx->IO.FontDefault == old_font)
3209
0
                ctx->IO.FontDefault = new_font;
3210
0
            if (ctx->Font == old_font)
3211
0
            {
3212
0
                ImGuiContext* curr_ctx = ImGui::GetCurrentContext();
3213
0
                bool need_bind_ctx = ctx != curr_ctx;
3214
0
                if (need_bind_ctx)
3215
0
                    ImGui::SetCurrentContext(ctx);
3216
0
                ImGui::SetCurrentFont(new_font, ctx->FontSizeBase, ctx->FontSize);
3217
0
                if (need_bind_ctx)
3218
0
                    ImGui::SetCurrentContext(curr_ctx);
3219
0
            }
3220
0
            for (ImFontStackData& font_stack_data : ctx->FontStack)
3221
0
                if (font_stack_data.Font == old_font)
3222
0
                    font_stack_data.Font = new_font;
3223
0
        }
3224
0
    }
3225
0
}
3226
3227
void ImFontAtlas::RemoveFont(ImFont* font)
3228
0
{
3229
0
    IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas!");
3230
0
    font->ClearOutputData();
3231
3232
0
    ImFontAtlasFontDestroyOutput(this, font);
3233
0
    for (ImFontConfig* src : font->Sources)
3234
0
        ImFontAtlasFontDestroySourceData(this, src);
3235
0
    for (int src_n = 0; src_n < Sources.Size; src_n++)
3236
0
        if (Sources[src_n].DstFont == font)
3237
0
            Sources.erase(&Sources[src_n--]);
3238
3239
0
    bool removed = Fonts.find_erase(font);
3240
0
    IM_ASSERT(removed);
3241
0
    IM_UNUSED(removed);
3242
3243
0
    ImFontAtlasBuildUpdatePointers(this);
3244
3245
0
    font->ContainerAtlas = NULL;
3246
0
    IM_DELETE(font);
3247
3248
    // Notify external systems
3249
0
    ImFont* new_current_font = Fonts.empty() ? NULL : Fonts[0];
3250
0
    ImFontAtlasBuildNotifySetFont(this, font, new_current_font);
3251
0
}
3252
3253
// At it is common to do an AddCustomRect() followed by a GetCustomRect(), we provide an optional 'ImFontAtlasRect* out_r = NULL' argument to retrieve the info straight away.
3254
ImFontAtlasRectId ImFontAtlas::AddCustomRect(int width, int height, ImFontAtlasRect* out_r)
3255
2
{
3256
2
    IM_ASSERT(width > 0 && width <= 0xFFFF);
3257
2
    IM_ASSERT(height > 0 && height <= 0xFFFF);
3258
3259
2
    if (Builder == NULL)
3260
0
        ImFontAtlasBuildInit(this);
3261
3262
2
    ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);
3263
2
    if (r_id == ImFontAtlasRectId_Invalid)
3264
0
        return ImFontAtlasRectId_Invalid;
3265
2
    if (out_r != NULL)
3266
2
        GetCustomRect(r_id, out_r);
3267
3268
2
    if (RendererHasTextures)
3269
2
    {
3270
2
        ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id);
3271
2
        ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);
3272
2
    }
3273
2
    return r_id;
3274
2
}
3275
3276
void ImFontAtlas::RemoveCustomRect(ImFontAtlasRectId id)
3277
0
{
3278
0
    if (ImFontAtlasPackGetRectSafe(this, id) == NULL)
3279
0
        return;
3280
0
    ImFontAtlasPackDiscardRect(this, id);
3281
0
}
3282
3283
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3284
// This API does not make sense anymore with scalable fonts.
3285
// - Prefer adding a font source (ImFontConfig) using a custom/procedural loader.
3286
// - You may use ImFontFlags_LockBakedSizes to limit an existing font to known baked sizes:
3287
//     ImFont* myfont = io.Fonts->AddFontFromFileTTF(....);
3288
//     myfont->GetFontBaked(16.0f);
3289
//     myfont->Flags |= ImFontFlags_LockBakedSizes;
3290
ImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset)
3291
0
{
3292
0
    float font_size = font->LegacySize;
3293
0
    return AddCustomRectFontGlyphForSize(font, font_size, codepoint, width, height, advance_x, offset);
3294
0
}
3295
// FIXME: we automatically set glyph.Colored=true by default.
3296
// If you need to alter this, you can write 'font->Glyphs.back()->Colored' after calling AddCustomRectFontGlyph().
3297
ImFontAtlasRectId ImFontAtlas::AddCustomRectFontGlyphForSize(ImFont* font, float font_size, ImWchar codepoint, int width, int height, float advance_x, const ImVec2& offset)
3298
0
{
3299
#ifdef IMGUI_USE_WCHAR32
3300
    IM_ASSERT(codepoint <= IM_UNICODE_CODEPOINT_MAX);
3301
#endif
3302
0
    IM_ASSERT(font != NULL);
3303
0
    IM_ASSERT(width > 0 && width <= 0xFFFF);
3304
0
    IM_ASSERT(height > 0 && height <= 0xFFFF);
3305
3306
0
    ImFontBaked* baked = font->GetFontBaked(font_size);
3307
3308
0
    ImFontAtlasRectId r_id = ImFontAtlasPackAddRect(this, width, height);
3309
0
    if (r_id == ImFontAtlasRectId_Invalid)
3310
0
        return ImFontAtlasRectId_Invalid;
3311
0
    ImTextureRect* r = ImFontAtlasPackGetRect(this, r_id);
3312
0
    if (RendererHasTextures)
3313
0
        ImFontAtlasTextureBlockQueueUpload(this, TexData, r->x, r->y, r->w, r->h);
3314
3315
0
    if (baked->IsGlyphLoaded(codepoint))
3316
0
        ImFontAtlasBakedDiscardFontGlyph(this, font, baked, baked->FindGlyph(codepoint));
3317
3318
0
    ImFontGlyph glyph;
3319
0
    glyph.Codepoint = codepoint;
3320
0
    glyph.AdvanceX = advance_x;
3321
0
    glyph.X0 = offset.x;
3322
0
    glyph.Y0 = offset.y;
3323
0
    glyph.X1 = offset.x + r->w;
3324
0
    glyph.Y1 = offset.y + r->h;
3325
0
    glyph.Visible = true;
3326
0
    glyph.Colored = true; // FIXME: Arbitrary
3327
0
    glyph.PackId = r_id;
3328
0
    ImFontAtlasBakedAddFontGlyph(this, baked, font->Sources[0], &glyph);
3329
0
    return r_id;
3330
0
}
3331
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3332
3333
bool ImFontAtlas::GetCustomRect(ImFontAtlasRectId id, ImFontAtlasRect* out_r) const
3334
4
{
3335
4
    ImTextureRect* r = ImFontAtlasPackGetRectSafe((ImFontAtlas*)this, id);
3336
4
    if (r == NULL)
3337
2
        return false;
3338
2
    IM_ASSERT(TexData->Width > 0 && TexData->Height > 0);   // Font atlas needs to be built before we can calculate UV coordinates
3339
2
    if (out_r == NULL)
3340
0
        return true;
3341
2
    out_r->x = r->x;
3342
2
    out_r->y = r->y;
3343
2
    out_r->w = r->w;
3344
2
    out_r->h = r->h;
3345
2
    out_r->uv0 = ImVec2((float)(r->x), (float)(r->y)) * TexUvScale;
3346
2
    out_r->uv1 = ImVec2((float)(r->x + r->w), (float)(r->y + r->h)) * TexUvScale;
3347
2
    return true;
3348
2
}
3349
3350
bool ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2])
3351
0
{
3352
0
    if (cursor_type <= ImGuiMouseCursor_None || cursor_type >= ImGuiMouseCursor_COUNT)
3353
0
        return false;
3354
0
    if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors)
3355
0
        return false;
3356
3357
0
    ImTextureRect* r = ImFontAtlasPackGetRect(atlas, atlas->Builder->PackIdMouseCursors);
3358
0
    ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->x, (float)r->y);
3359
0
    ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1];
3360
0
    *out_size = size;
3361
0
    *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2];
3362
0
    out_uv_border[0] = (pos) * atlas->TexUvScale;
3363
0
    out_uv_border[1] = (pos + size) * atlas->TexUvScale;
3364
0
    pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;
3365
0
    out_uv_fill[0] = (pos) * atlas->TexUvScale;
3366
0
    out_uv_fill[1] = (pos + size) * atlas->TexUvScale;
3367
0
    return true;
3368
0
}
3369
3370
// When atlas->RendererHasTextures = true, this is only called if no font were loaded.
3371
void ImFontAtlasBuildMain(ImFontAtlas* atlas)
3372
1
{
3373
1
    IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!");
3374
1
    if (atlas->TexData && atlas->TexData->Format != atlas->TexDesiredFormat)
3375
0
        ImFontAtlasBuildClear(atlas);
3376
3377
1
    if (atlas->Builder == NULL)
3378
1
        ImFontAtlasBuildInit(atlas);
3379
3380
    // Default font is none are specified
3381
1
    if (atlas->Sources.Size == 0)
3382
1
        atlas->AddFontDefault();
3383
3384
    // [LEGACY] For backends not supporting RendererHasTextures: preload all glyphs
3385
1
    ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas);
3386
1
    if (atlas->RendererHasTextures == false) // ~ImGuiBackendFlags_RendererHasTextures
3387
0
        ImFontAtlasBuildLegacyPreloadAllGlyphRanges(atlas);
3388
1
    atlas->TexIsBuilt = true;
3389
1
}
3390
3391
void ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v)
3392
22
{
3393
    // Automatically disable horizontal oversampling over size 36
3394
22
    const float raster_size = baked->Size * baked->RasterizerDensity * src->RasterizerDensity;
3395
22
    *out_oversample_h = (src->OversampleH != 0) ? src->OversampleH : (raster_size > 36.0f || src->PixelSnapH) ? 1 : 2;
3396
22
    *out_oversample_v = (src->OversampleV != 0) ? src->OversampleV : 1;
3397
22
}
3398
3399
// Setup main font loader for the atlas
3400
// Every font source (ImFontConfig) will use this unless ImFontConfig::FontLoader specify a custom loader.
3401
void ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader)
3402
1
{
3403
1
    if (atlas->FontLoader == font_loader)
3404
0
        return;
3405
1
    IM_ASSERT(!atlas->Locked && "Cannot modify a locked ImFontAtlas!");
3406
3407
1
    for (ImFont* font : atlas->Fonts)
3408
0
        ImFontAtlasFontDestroyOutput(atlas, font);
3409
1
    if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderShutdown)
3410
0
        atlas->FontLoader->LoaderShutdown(atlas);
3411
3412
1
    atlas->FontLoader = font_loader;
3413
1
    atlas->FontLoaderName = font_loader ? font_loader->Name : "NULL";
3414
1
    IM_ASSERT(atlas->FontLoaderData == NULL);
3415
3416
1
    if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderInit)
3417
0
        atlas->FontLoader->LoaderInit(atlas);
3418
1
    for (ImFont* font : atlas->Fonts)
3419
0
        ImFontAtlasFontInitOutput(atlas, font);
3420
1
    for (ImFont* font : atlas->Fonts)
3421
0
        for (ImFontConfig* src : font->Sources)
3422
0
            ImFontAtlasFontSourceAddToFont(atlas, font, src);
3423
1
}
3424
3425
// Preload all glyph ranges for legacy backends.
3426
// This may lead to multiple texture creation which might be a little slower than before.
3427
void ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas)
3428
0
{
3429
0
    atlas->Builder->PreloadedAllGlyphsRanges = true;
3430
0
    for (ImFont* font : atlas->Fonts)
3431
0
    {
3432
0
        ImFontBaked* baked = font->GetFontBaked(font->LegacySize);
3433
0
        if (font->FallbackChar != 0)
3434
0
            baked->FindGlyph(font->FallbackChar);
3435
0
        if (font->EllipsisChar != 0)
3436
0
            baked->FindGlyph(font->EllipsisChar);
3437
0
        for (ImFontConfig* src : font->Sources)
3438
0
        {
3439
0
            const ImWchar* ranges = src->GlyphRanges ? src->GlyphRanges : atlas->GetGlyphRangesDefault();
3440
0
            for (; ranges[0]; ranges += 2)
3441
0
                for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560
3442
0
                    baked->FindGlyph((ImWchar)c);
3443
0
        }
3444
0
    }
3445
0
}
3446
3447
// FIXME: May make ImFont::Sources a ImSpan<> and move ownership to ImFontAtlas
3448
void ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas)
3449
2
{
3450
2
    for (ImFont* font : atlas->Fonts)
3451
1
        font->Sources.resize(0);
3452
2
    for (ImFontConfig& src : atlas->Sources)
3453
1
        src.DstFont->Sources.push_back(&src);
3454
2
}
3455
3456
// Render a white-colored bitmap encoded in a string
3457
void ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char)
3458
2
{
3459
2
    ImTextureData* tex = atlas->TexData;
3460
2
    IM_ASSERT(x >= 0 && x + w <= tex->Width);
3461
2
    IM_ASSERT(y >= 0 && y + h <= tex->Height);
3462
3463
2
    switch (tex->Format)
3464
2
    {
3465
0
    case ImTextureFormat_Alpha8:
3466
0
    {
3467
0
        ImU8* out_p = (ImU8*)tex->GetPixelsAt(x, y);
3468
0
        for (int off_y = 0; off_y < h; off_y++, out_p += tex->Width, in_str += w)
3469
0
            for (int off_x = 0; off_x < w; off_x++)
3470
0
                out_p[off_x] = (in_str[off_x] == in_marker_char) ? 0xFF : 0x00;
3471
0
        break;
3472
0
    }
3473
2
    case ImTextureFormat_RGBA32:
3474
2
    {
3475
2
        ImU32* out_p = (ImU32*)tex->GetPixelsAt(x, y);
3476
56
        for (int off_y = 0; off_y < h; off_y++, out_p += tex->Width, in_str += w)
3477
6.64k
            for (int off_x = 0; off_x < w; off_x++)
3478
6.58k
                out_p[off_x] = (in_str[off_x] == in_marker_char) ? IM_COL32_WHITE : IM_COL32_BLACK_TRANS;
3479
2
        break;
3480
0
    }
3481
2
    }
3482
2
}
3483
3484
static void ImFontAtlasBuildUpdateBasicTexData(ImFontAtlas* atlas)
3485
1
{
3486
    // Pack and store identifier so we can refresh UV coordinates on texture resize.
3487
    // FIXME-NEWATLAS: User/custom rects where user code wants to store UV coordinates will need to do the same thing.
3488
1
    ImFontAtlasBuilder* builder = atlas->Builder;
3489
1
    ImVec2i pack_size = (atlas->Flags & ImFontAtlasFlags_NoMouseCursors) ? ImVec2i(2, 2) : ImVec2i(FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
3490
3491
1
    ImFontAtlasRect r;
3492
1
    bool add_and_draw = (atlas->GetCustomRect(builder->PackIdMouseCursors, &r) == false);
3493
1
    if (add_and_draw)
3494
1
    {
3495
1
        builder->PackIdMouseCursors = atlas->AddCustomRect(pack_size.x, pack_size.y, &r);
3496
1
        IM_ASSERT(builder->PackIdMouseCursors != ImFontAtlasRectId_Invalid);
3497
3498
        // Draw to texture
3499
1
        if (atlas->Flags & ImFontAtlasFlags_NoMouseCursors)
3500
0
        {
3501
            // 2x2 white pixels
3502
0
            ImFontAtlasBuildRenderBitmapFromString(atlas, r.x, r.y, 2, 2, "XX" "XX", 'X');
3503
0
        }
3504
1
        else
3505
1
        {
3506
            // 2x2 white pixels + mouse cursors
3507
1
            const int x_for_white = r.x;
3508
1
            const int x_for_black = r.x + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;
3509
1
            ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_white, r.y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.');
3510
1
            ImFontAtlasBuildRenderBitmapFromString(atlas, x_for_black, r.y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X');
3511
1
        }
3512
1
    }
3513
3514
    // Refresh UV coordinates
3515
1
    atlas->TexUvWhitePixel = ImVec2((r.x + 0.5f) * atlas->TexUvScale.x, (r.y + 0.5f) * atlas->TexUvScale.y);
3516
1
}
3517
3518
static void ImFontAtlasBuildUpdateLinesTexData(ImFontAtlas* atlas)
3519
1
{
3520
1
    if (atlas->Flags & ImFontAtlasFlags_NoBakedLines)
3521
0
        return;
3522
3523
    // Pack and store identifier so we can refresh UV coordinates on texture resize.
3524
1
    ImTextureData* tex = atlas->TexData;
3525
1
    ImFontAtlasBuilder* builder = atlas->Builder;
3526
3527
1
    ImFontAtlasRect r;
3528
1
    bool add_and_draw = atlas->GetCustomRect(builder->PackIdLinesTexData, &r) == false;
3529
1
    if (add_and_draw)
3530
1
    {
3531
1
        ImVec2i pack_size = ImVec2i(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);
3532
1
        builder->PackIdLinesTexData = atlas->AddCustomRect(pack_size.x, pack_size.y, &r);
3533
1
        IM_ASSERT(builder->PackIdLinesTexData != ImFontAtlasRectId_Invalid);
3534
1
    }
3535
3536
    // Register texture region for thick lines
3537
    // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row
3538
    // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them
3539
34
    for (int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row
3540
33
    {
3541
        // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle
3542
33
        const int y = n;
3543
33
        const int line_width = n;
3544
33
        const int pad_left = (r.w - line_width) / 2;
3545
33
        const int pad_right = r.w - (pad_left + line_width);
3546
33
        IM_ASSERT(pad_left + line_width + pad_right == r.w && y < r.h); // Make sure we're inside the texture bounds before we start writing pixels
3547
3548
        // Write each slice
3549
33
        if (add_and_draw && tex->Format == ImTextureFormat_Alpha8)
3550
0
        {
3551
0
            ImU8* write_ptr = (ImU8*)tex->GetPixelsAt(r.x, r.y + y);
3552
0
            for (int i = 0; i < pad_left; i++)
3553
0
                *(write_ptr + i) = 0x00;
3554
3555
0
            for (int i = 0; i < line_width; i++)
3556
0
                *(write_ptr + pad_left + i) = 0xFF;
3557
3558
0
            for (int i = 0; i < pad_right; i++)
3559
0
                *(write_ptr + pad_left + line_width + i) = 0x00;
3560
0
        }
3561
33
        else if (add_and_draw && tex->Format == ImTextureFormat_RGBA32)
3562
33
        {
3563
33
            ImU32* write_ptr = (ImU32*)(void*)tex->GetPixelsAt(r.x, r.y + y);
3564
322
            for (int i = 0; i < pad_left; i++)
3565
289
                *(write_ptr + i) = IM_COL32(255, 255, 255, 0);
3566
3567
561
            for (int i = 0; i < line_width; i++)
3568
528
                *(write_ptr + pad_left + i) = IM_COL32_WHITE;
3569
3570
338
            for (int i = 0; i < pad_right; i++)
3571
305
                *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0);
3572
33
        }
3573
3574
        // Refresh UV coordinates
3575
33
        ImVec2 uv0 = ImVec2((float)(r.x + pad_left - 1), (float)(r.y + y)) * atlas->TexUvScale;
3576
33
        ImVec2 uv1 = ImVec2((float)(r.x + pad_left + line_width + 1), (float)(r.y + y + 1)) * atlas->TexUvScale;
3577
33
        float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts
3578
33
        atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v);
3579
33
    }
3580
1
}
3581
3582
//-----------------------------------------------------------------------------------------------------------------------------
3583
3584
// Was tempted to lazily init FontSrc but wouldn't save much + makes it more complicated to detect invalid data at AddFont()
3585
bool ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font)
3586
0
{
3587
0
    bool ret = true;
3588
0
    for (ImFontConfig* src : font->Sources)
3589
0
        if (!ImFontAtlasFontSourceInit(atlas, src))
3590
0
            ret = false;
3591
0
    IM_ASSERT(ret); // Unclear how to react to this meaningfully. Assume that result will be same as initial AddFont() call.
3592
0
    return ret;
3593
0
}
3594
3595
// Keep source/input FontData
3596
void ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font)
3597
2
{
3598
2
    font->ClearOutputData();
3599
2
    for (ImFontConfig* src : font->Sources)
3600
2
    {
3601
2
        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
3602
2
        if (loader && loader->FontSrcDestroy != NULL)
3603
2
            loader->FontSrcDestroy(atlas, src);
3604
2
    }
3605
2
}
3606
3607
//-----------------------------------------------------------------------------------------------------------------------------
3608
3609
bool ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src)
3610
1
{
3611
1
    const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
3612
1
    if (loader->FontSrcInit != NULL && !loader->FontSrcInit(atlas, src))
3613
0
        return false;
3614
1
    return true;
3615
1
}
3616
3617
void ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src)
3618
1
{
3619
1
    if (src->MergeMode == false)
3620
1
    {
3621
1
        font->ClearOutputData();
3622
        //font->FontSize = src->SizePixels;
3623
1
        font->ContainerAtlas = atlas;
3624
1
        IM_ASSERT(font->Sources[0] == src);
3625
1
    }
3626
1
    atlas->TexIsBuilt = false; // For legacy backends
3627
1
    ImFontAtlasBuildSetupFontSpecialGlyphs(atlas, font, src);
3628
1
}
3629
3630
void ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src)
3631
1
{
3632
1
    IM_UNUSED(atlas);
3633
1
    if (src->FontDataOwnedByAtlas)
3634
1
        IM_FREE(src->FontData);
3635
1
    src->FontData = NULL;
3636
1
    if (src->GlyphExcludeRanges)
3637
0
        IM_FREE((void*)src->GlyphExcludeRanges);
3638
1
    src->GlyphExcludeRanges = NULL;
3639
1
}
3640
3641
// Create a compact, baked "..." if it doesn't exist, by using the ".".
3642
// This may seem overly complicated right now but the point is to exercise and improve a technique which should be increasingly used.
3643
// FIXME-NEWATLAS: This borrows too much from FontLoader's FontLoadGlyph() handlers and suggest that we should add further helpers.
3644
static ImFontGlyph* ImFontAtlasBuildSetupFontBakedEllipsis(ImFontAtlas* atlas, ImFontBaked* baked)
3645
0
{
3646
0
    ImFont* font = baked->ContainerFont;
3647
0
    IM_ASSERT(font->EllipsisChar != 0);
3648
3649
0
    const ImFontGlyph* dot_glyph = baked->FindGlyphNoFallback((ImWchar)'.');
3650
0
    if (dot_glyph == NULL)
3651
0
        dot_glyph = baked->FindGlyphNoFallback((ImWchar)0xFF0E);
3652
0
    if (dot_glyph == NULL)
3653
0
        return NULL;
3654
0
    ImFontAtlasRectId dot_r_id = dot_glyph->PackId; // Deep copy to avoid invalidation of glyphs and rect pointers
3655
0
    ImTextureRect* dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id);
3656
0
    const int dot_spacing = 1;
3657
0
    const float dot_step = (dot_glyph->X1 - dot_glyph->X0) + dot_spacing;
3658
3659
0
    ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, (dot_r->w * 3 + dot_spacing * 2), dot_r->h);
3660
0
    ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);
3661
3662
0
    ImFontGlyph glyph_in = {};
3663
0
    ImFontGlyph* glyph = &glyph_in;
3664
0
    glyph->Codepoint = font->EllipsisChar;
3665
0
    glyph->AdvanceX = ImMax(dot_glyph->AdvanceX, dot_glyph->X0 + dot_step * 3.0f - dot_spacing); // FIXME: Slightly odd for normally mono-space fonts but since this is used for trailing contents.
3666
0
    glyph->X0 = dot_glyph->X0;
3667
0
    glyph->Y0 = dot_glyph->Y0;
3668
0
    glyph->X1 = dot_glyph->X0 + dot_step * 3 - dot_spacing;
3669
0
    glyph->Y1 = dot_glyph->Y1;
3670
0
    glyph->Visible = true;
3671
0
    glyph->PackId = pack_id;
3672
0
    glyph = ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, glyph);
3673
0
    dot_glyph = NULL; // Invalidated
3674
3675
    // Copy to texture, post-process and queue update for backend
3676
    // FIXME-NEWATLAS-V2: Dot glyph is already post-processed as this point, so this would damage it.
3677
0
    dot_r = ImFontAtlasPackGetRect(atlas, dot_r_id);
3678
0
    ImTextureData* tex = atlas->TexData;
3679
0
    for (int n = 0; n < 3; n++)
3680
0
        ImFontAtlasTextureBlockCopy(tex, dot_r->x, dot_r->y, tex, r->x + (dot_r->w + dot_spacing) * n, r->y, dot_r->w, dot_r->h);
3681
0
    ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);
3682
3683
0
    return glyph;
3684
0
}
3685
3686
// Load fallback in order to obtain its index
3687
// (this is called from in hot-path so we avoid extraneous parameters to minimize impact on code size)
3688
static void ImFontAtlasBuildSetupFontBakedFallback(ImFontBaked* baked)
3689
0
{
3690
0
    IM_ASSERT(baked->FallbackGlyphIndex == -1);
3691
0
    IM_ASSERT(baked->FallbackAdvanceX == 0.0f);
3692
0
    ImFont* font = baked->ContainerFont;
3693
0
    ImFontGlyph* fallback_glyph = NULL;
3694
0
    if (font->FallbackChar != 0)
3695
0
        fallback_glyph = baked->FindGlyphNoFallback(font->FallbackChar);
3696
0
    if (fallback_glyph == NULL)
3697
0
    {
3698
0
        ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' ');
3699
0
        ImFontGlyph glyph;
3700
0
        glyph.Codepoint = 0;
3701
0
        glyph.AdvanceX = space_glyph ? space_glyph->AdvanceX : IM_ROUND(baked->Size * 0.40f);
3702
0
        fallback_glyph = ImFontAtlasBakedAddFontGlyph(font->ContainerAtlas, baked, NULL, &glyph);
3703
0
    }
3704
0
    baked->FallbackGlyphIndex = baked->Glyphs.index_from_ptr(fallback_glyph); // Storing index avoid need to update pointer on growth and simplify inner loop code
3705
0
    baked->FallbackAdvanceX = fallback_glyph->AdvanceX;
3706
0
}
3707
3708
static void ImFontAtlasBuildSetupFontBakedBlanks(ImFontAtlas* atlas, ImFontBaked* baked)
3709
1
{
3710
    // Mark space as always hidden (not strictly correct/necessary. but some e.g. icons fonts don't have a space. it tends to look neater in previews)
3711
1
    ImFontGlyph* space_glyph = baked->FindGlyphNoFallback((ImWchar)' ');
3712
1
    if (space_glyph != NULL)
3713
1
        space_glyph->Visible = false;
3714
3715
    // Setup Tab character.
3716
    // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
3717
1
    if (baked->FindGlyphNoFallback('\t') == NULL && space_glyph != NULL)
3718
0
    {
3719
0
        ImFontGlyph tab_glyph;
3720
0
        tab_glyph.Codepoint = '\t';
3721
0
        tab_glyph.AdvanceX = space_glyph->AdvanceX * IM_TABSIZE;
3722
0
        ImFontAtlasBakedAddFontGlyph(atlas, baked, NULL, &tab_glyph);
3723
0
    }
3724
1
}
3725
3726
// Load/identify special glyphs
3727
// (note that this is called again for fonts with MergeMode)
3728
void ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src)
3729
1
{
3730
1
    IM_UNUSED(atlas);
3731
1
    IM_ASSERT(font->Sources.contains(src));
3732
3733
    // Find Fallback character. Actual glyph loaded in GetFontBaked().
3734
1
    const ImWchar fallback_chars[] = { font->FallbackChar, (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };
3735
1
    if (font->FallbackChar == 0)
3736
1
        for (ImWchar candidate_char : fallback_chars)
3737
3
            if (candidate_char != 0 && font->IsGlyphInFont(candidate_char))
3738
1
            {
3739
1
                font->FallbackChar = (ImWchar)candidate_char;
3740
1
                break;
3741
1
            }
3742
3743
    // Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
3744
    // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
3745
    // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.
3746
1
    const ImWchar ellipsis_chars[] = { src->EllipsisChar, (ImWchar)0x2026, (ImWchar)0x0085 };
3747
1
    if (font->EllipsisChar == 0)
3748
1
        for (ImWchar candidate_char : ellipsis_chars)
3749
1
            if (candidate_char != 0 && font->IsGlyphInFont(candidate_char))
3750
1
            {
3751
1
                font->EllipsisChar = candidate_char;
3752
1
                break;
3753
1
            }
3754
1
    if (font->EllipsisChar == 0)
3755
0
    {
3756
0
        font->EllipsisChar = 0x0085;
3757
0
        font->EllipsisAutoBake = true;
3758
0
    }
3759
1
}
3760
3761
void ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph)
3762
0
{
3763
0
    if (glyph->PackId != ImFontAtlasRectId_Invalid)
3764
0
    {
3765
0
        ImFontAtlasPackDiscardRect(atlas, glyph->PackId);
3766
0
        glyph->PackId = ImFontAtlasRectId_Invalid;
3767
0
    }
3768
0
    ImWchar c = (ImWchar)glyph->Codepoint;
3769
0
    IM_ASSERT(font->FallbackChar != c && font->EllipsisChar != c); // Unsupported for simplicity
3770
0
    IM_ASSERT(glyph >= baked->Glyphs.Data && glyph < baked->Glyphs.Data + baked->Glyphs.Size);
3771
0
    IM_UNUSED(font);
3772
0
    baked->IndexLookup[c] = IM_FONTGLYPH_INDEX_UNUSED;
3773
0
    baked->IndexAdvanceX[c] = baked->FallbackAdvanceX;
3774
0
}
3775
3776
ImFontBaked* ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id)
3777
1
{
3778
1
    IMGUI_DEBUG_LOG_FONT("[font] Created baked %.2fpx\n", font_size);
3779
1
    ImFontBaked* baked = atlas->Builder->BakedPool.push_back(ImFontBaked());
3780
1
    baked->Size = font_size;
3781
1
    baked->RasterizerDensity = font_rasterizer_density;
3782
1
    baked->BakedId = baked_id;
3783
1
    baked->ContainerFont = font;
3784
1
    baked->LastUsedFrame = atlas->Builder->FrameCount;
3785
3786
    // Initialize backend data
3787
1
    size_t loader_data_size = 0;
3788
1
    for (ImFontConfig* src : font->Sources) // Cannot easily be cached as we allow changing backend
3789
1
    {
3790
1
        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
3791
1
        loader_data_size += loader->FontBakedSrcLoaderDataSize;
3792
1
    }
3793
1
    baked->FontLoaderDatas = (loader_data_size > 0) ? IM_ALLOC(loader_data_size) : NULL;
3794
1
    char* loader_data_p = (char*)baked->FontLoaderDatas;
3795
1
    for (ImFontConfig* src : font->Sources)
3796
1
    {
3797
1
        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
3798
1
        if (loader->FontBakedInit)
3799
1
            loader->FontBakedInit(atlas, src, baked, loader_data_p);
3800
1
        loader_data_p += loader->FontBakedSrcLoaderDataSize;
3801
1
    }
3802
3803
1
    ImFontAtlasBuildSetupFontBakedBlanks(atlas, baked);
3804
1
    return baked;
3805
1
}
3806
3807
// FIXME-OPT: This is not a fast query. Adding a BakedCount field in Font might allow to take a shortcut for the most common case.
3808
ImFontBaked* ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density)
3809
0
{
3810
0
    ImFontAtlasBuilder* builder = atlas->Builder;
3811
0
    for (int step_n = 0; step_n < 2; step_n++)
3812
0
    {
3813
0
        ImFontBaked* closest_larger_match = NULL;
3814
0
        ImFontBaked* closest_smaller_match = NULL;
3815
0
        for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
3816
0
        {
3817
0
            ImFontBaked* baked = &builder->BakedPool[baked_n];
3818
0
            if (baked->ContainerFont != font || baked->WantDestroy)
3819
0
                continue;
3820
0
            if (step_n == 0 && baked->RasterizerDensity != font_rasterizer_density) // First try with same density
3821
0
                continue;
3822
0
            if (baked->Size > font_size && (closest_larger_match == NULL || baked->Size < closest_larger_match->Size))
3823
0
                closest_larger_match = baked;
3824
0
            if (baked->Size < font_size && (closest_smaller_match == NULL || baked->Size > closest_smaller_match->Size))
3825
0
                closest_smaller_match = baked;
3826
0
        }
3827
0
        if (closest_larger_match)
3828
0
            if (closest_smaller_match == NULL || (closest_larger_match->Size >= font_size * 2.0f && closest_smaller_match->Size > font_size * 0.5f))
3829
0
                return closest_larger_match;
3830
0
        if (closest_smaller_match)
3831
0
            return closest_smaller_match;
3832
0
    }
3833
0
    return NULL;
3834
0
}
3835
3836
void ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked)
3837
1
{
3838
1
    ImFontAtlasBuilder* builder = atlas->Builder;
3839
1
    IMGUI_DEBUG_LOG_FONT("[font] Discard baked %.2f for \"%s\"\n", baked->Size, font->GetDebugName());
3840
3841
1
    for (ImFontGlyph& glyph : baked->Glyphs)
3842
22
        if (glyph.PackId != ImFontAtlasRectId_Invalid)
3843
20
            ImFontAtlasPackDiscardRect(atlas, glyph.PackId);
3844
3845
1
    char* loader_data_p = (char*)baked->FontLoaderDatas;
3846
1
    for (ImFontConfig* src : font->Sources)
3847
1
    {
3848
1
        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
3849
1
        if (loader->FontBakedDestroy)
3850
0
            loader->FontBakedDestroy(atlas, src, baked, loader_data_p);
3851
1
        loader_data_p += loader->FontBakedSrcLoaderDataSize;
3852
1
    }
3853
1
    if (baked->FontLoaderDatas)
3854
0
    {
3855
0
        IM_FREE(baked->FontLoaderDatas);
3856
0
        baked->FontLoaderDatas = NULL;
3857
0
    }
3858
1
    builder->BakedMap.SetVoidPtr(baked->BakedId, NULL);
3859
1
    builder->BakedDiscardedCount++;
3860
1
    baked->ClearOutputData();
3861
1
    baked->WantDestroy = true;
3862
1
    font->LastBaked = NULL;
3863
1
}
3864
3865
// use unused_frames==0 to discard everything.
3866
void ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames)
3867
3
{
3868
3
    if (ImFontAtlasBuilder* builder = atlas->Builder) // This can be called from font destructor
3869
2
        for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
3870
1
        {
3871
1
            ImFontBaked* baked = &builder->BakedPool[baked_n];
3872
1
            if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)
3873
0
                continue;
3874
1
            if (baked->ContainerFont != font || baked->WantDestroy)
3875
0
                continue;
3876
1
            ImFontAtlasBakedDiscard(atlas, font, baked);
3877
1
        }
3878
3
}
3879
3880
// use unused_frames==0 to discard everything.
3881
void ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames)
3882
0
{
3883
0
    ImFontAtlasBuilder* builder = atlas->Builder;
3884
0
    for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
3885
0
    {
3886
0
        ImFontBaked* baked = &builder->BakedPool[baked_n];
3887
0
        if (baked->LastUsedFrame + unused_frames > atlas->Builder->FrameCount)
3888
0
            continue;
3889
0
        if (baked->WantDestroy || (baked->ContainerFont->Flags & ImFontFlags_LockBakedSizes))
3890
0
            continue;
3891
0
        ImFontAtlasBakedDiscard(atlas, baked->ContainerFont, baked);
3892
0
    }
3893
0
}
3894
3895
// Those functions are designed to facilitate changing the underlying structures for ImFontAtlas to store an array of ImDrawListSharedData*
3896
void ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data)
3897
1
{
3898
1
    IM_ASSERT(!atlas->DrawListSharedDatas.contains(data));
3899
1
    atlas->DrawListSharedDatas.push_back(data);
3900
1
}
3901
3902
void ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data)
3903
1
{
3904
1
    IM_ASSERT(atlas->DrawListSharedDatas.contains(data));
3905
1
    atlas->DrawListSharedDatas.find_erase(data);
3906
1
}
3907
3908
// Update texture identifier in all active draw lists
3909
void ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex)
3910
1
{
3911
1
    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
3912
1
        for (ImDrawList* draw_list : shared_data->DrawLists)
3913
0
        {
3914
            // Replace in command-buffer
3915
            // (there is not need to replace in ImDrawListSplitter: current channel is in ImDrawList's CmdBuffer[],
3916
            //  other channels will be on SetCurrentChannel() which already needs to compare CmdHeader anyhow)
3917
0
            if (draw_list->CmdBuffer.Size > 0 && draw_list->_CmdHeader.TexRef == old_tex)
3918
0
                draw_list->_SetTexture(new_tex);
3919
3920
            // Replace in stack
3921
0
            for (ImTextureRef& stacked_tex : draw_list->_TextureStack)
3922
0
                if (stacked_tex == old_tex)
3923
0
                    stacked_tex = new_tex;
3924
0
        }
3925
1
}
3926
3927
// Update texture coordinates in all draw list shared context
3928
// FIXME-NEWATLAS FIXME-OPT: Doesn't seem necessary to update for all, only one bound to current context?
3929
void ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas)
3930
161k
{
3931
161k
    for (ImDrawListSharedData* shared_data : atlas->DrawListSharedDatas)
3932
161k
        if (shared_data->FontAtlas == atlas)
3933
161k
        {
3934
161k
            shared_data->TexUvWhitePixel = atlas->TexUvWhitePixel;
3935
161k
            shared_data->TexUvLines = atlas->TexUvLines;
3936
161k
        }
3937
161k
}
3938
3939
// Set current texture. This is mostly called from AddTexture() + to handle a failed resize.
3940
static void ImFontAtlasBuildSetTexture(ImFontAtlas* atlas, ImTextureData* tex)
3941
1
{
3942
1
    ImTextureRef old_tex_ref = atlas->TexRef;
3943
1
    atlas->TexData = tex;
3944
1
    atlas->TexUvScale = ImVec2(1.0f / tex->Width, 1.0f / tex->Height);
3945
1
    atlas->TexRef._TexData = tex;
3946
    //atlas->TexRef._TexID = tex->TexID; // <-- We intentionally don't do that. It would be misleading and betray promise that both fields aren't set.
3947
1
    ImFontAtlasUpdateDrawListsTextures(atlas, old_tex_ref, atlas->TexRef);
3948
1
}
3949
3950
// Create a new texture, discard previous one
3951
ImTextureData* ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h)
3952
1
{
3953
1
    ImTextureData* old_tex = atlas->TexData;
3954
1
    ImTextureData* new_tex;
3955
3956
    // FIXME: Cannot reuse texture because old UV may have been used already (unless we remap UV).
3957
    /*if (old_tex != NULL && old_tex->Status == ImTextureStatus_WantCreate)
3958
    {
3959
        // Reuse texture not yet used by backend.
3960
        IM_ASSERT(old_tex->TexID == ImTextureID_Invalid && old_tex->BackendUserData == NULL);
3961
        old_tex->DestroyPixels();
3962
        old_tex->Updates.clear();
3963
        new_tex = old_tex;
3964
        old_tex = NULL;
3965
    }
3966
    else*/
3967
1
    {
3968
        // Add new
3969
1
        new_tex = IM_NEW(ImTextureData)();
3970
1
        new_tex->UniqueID = atlas->TexNextUniqueID++;
3971
1
        atlas->TexList.push_back(new_tex);
3972
1
    }
3973
1
    if (old_tex != NULL)
3974
0
    {
3975
        // Queue old as to destroy next frame
3976
0
        old_tex->WantDestroyNextFrame = true;
3977
0
        IM_ASSERT(old_tex->Status == ImTextureStatus_OK || old_tex->Status == ImTextureStatus_WantCreate || old_tex->Status == ImTextureStatus_WantUpdates);
3978
0
    }
3979
3980
1
    new_tex->Create(atlas->TexDesiredFormat, w, h);
3981
1
    atlas->TexIsBuilt = false;
3982
3983
1
    ImFontAtlasBuildSetTexture(atlas, new_tex);
3984
3985
1
    return new_tex;
3986
1
}
3987
3988
#if 0
3989
#define STB_IMAGE_WRITE_IMPLEMENTATION
3990
#include "../stb/stb_image_write.h"
3991
static void ImFontAtlasDebugWriteTexToDisk(ImTextureData* tex, const char* description)
3992
{
3993
    ImGuiContext& g = *GImGui;
3994
    char buf[128];
3995
    ImFormatString(buf, IM_ARRAYSIZE(buf), "[%05d] Texture #%03d - %s.png", g.FrameCount, tex->UniqueID, description);
3996
    stbi_write_png(buf, tex->Width, tex->Height, tex->BytesPerPixel, tex->Pixels, tex->GetPitch()); // tex->BytesPerPixel is technically not component, but ok for the formats we support.
3997
}
3998
#endif
3999
4000
void ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h)
4001
0
{
4002
0
    ImFontAtlasBuilder* builder = atlas->Builder;
4003
0
    builder->LockDisableResize = true;
4004
4005
0
    ImTextureData* old_tex = atlas->TexData;
4006
0
    ImTextureData* new_tex = ImFontAtlasTextureAdd(atlas, w, h);
4007
0
    new_tex->UseColors = old_tex->UseColors;
4008
0
    IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize+repack %dx%d => Texture #%03d: %dx%d\n", old_tex->UniqueID, old_tex->Width, old_tex->Height, new_tex->UniqueID, new_tex->Width, new_tex->Height);
4009
    //for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
4010
    //    IMGUI_DEBUG_LOG_FONT("[font] - Baked %.2fpx, %d glyphs, want_destroy=%d\n", builder->BakedPool[baked_n].FontSize, builder->BakedPool[baked_n].Glyphs.Size, builder->BakedPool[baked_n].WantDestroy);
4011
    //IMGUI_DEBUG_LOG_FONT("[font] - Old packed rects: %d, area %d px\n", builder->RectsPackedCount, builder->RectsPackedSurface);
4012
    //ImFontAtlasDebugWriteTexToDisk(old_tex, "Before Pack");
4013
4014
    // Repack, lose discarded rectangle, copy pixels
4015
    // FIXME-NEWATLAS: This is unstable because packing order is based on RectsIndex
4016
    // FIXME-NEWATLAS-V2: Repacking in batch would be beneficial to packing heuristic, and fix stability.
4017
    // FIXME-NEWATLAS-TESTS: Test calling RepackTexture with size too small to fits existing rects.
4018
0
    ImFontAtlasPackInit(atlas);
4019
0
    ImVector<ImTextureRect> old_rects;
4020
0
    ImVector<ImFontAtlasRectEntry> old_index = builder->RectsIndex;
4021
0
    old_rects.swap(builder->Rects);
4022
4023
0
    for (ImFontAtlasRectEntry& index_entry : builder->RectsIndex)
4024
0
    {
4025
0
        if (index_entry.IsUsed == false)
4026
0
            continue;
4027
0
        ImTextureRect& old_r = old_rects[index_entry.TargetIndex];
4028
0
        if (old_r.w == 0 && old_r.h == 0)
4029
0
            continue;
4030
0
        ImFontAtlasRectId new_r_id = ImFontAtlasPackAddRect(atlas, old_r.w, old_r.h, &index_entry);
4031
0
        if (new_r_id == ImFontAtlasRectId_Invalid)
4032
0
        {
4033
            // Undo, grow texture and try repacking again.
4034
            // FIXME-NEWATLAS-TESTS: This is a very rarely exercised path! It needs to be automatically tested properly.
4035
0
            IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: resize failed. Will grow.\n", new_tex->UniqueID);
4036
0
            new_tex->WantDestroyNextFrame = true;
4037
0
            builder->Rects.swap(old_rects);
4038
0
            builder->RectsIndex = old_index;
4039
0
            ImFontAtlasBuildSetTexture(atlas, old_tex);
4040
0
            ImFontAtlasTextureGrow(atlas, w, h); // Recurse
4041
0
            return;
4042
0
        }
4043
0
        IM_ASSERT(ImFontAtlasRectId_GetIndex(new_r_id) == builder->RectsIndex.index_from_ptr(&index_entry));
4044
0
        ImTextureRect* new_r = ImFontAtlasPackGetRect(atlas, new_r_id);
4045
0
        ImFontAtlasTextureBlockCopy(old_tex, old_r.x, old_r.y, new_tex, new_r->x, new_r->y, new_r->w, new_r->h);
4046
0
    }
4047
0
    IM_ASSERT(old_rects.Size == builder->Rects.Size + builder->RectsDiscardedCount);
4048
0
    builder->RectsDiscardedCount = 0;
4049
0
    builder->RectsDiscardedSurface = 0;
4050
4051
    // Patch glyphs UV
4052
0
    for (int baked_n = 0; baked_n < builder->BakedPool.Size; baked_n++)
4053
0
        for (ImFontGlyph& glyph : builder->BakedPool[baked_n].Glyphs)
4054
0
            if (glyph.PackId != ImFontAtlasRectId_Invalid)
4055
0
            {
4056
0
                ImTextureRect* r = ImFontAtlasPackGetRect(atlas, glyph.PackId);
4057
0
                glyph.U0 = (r->x) * atlas->TexUvScale.x;
4058
0
                glyph.V0 = (r->y) * atlas->TexUvScale.y;
4059
0
                glyph.U1 = (r->x + r->w) * atlas->TexUvScale.x;
4060
0
                glyph.V1 = (r->y + r->h) * atlas->TexUvScale.y;
4061
0
            }
4062
4063
    // Update other cached UV
4064
0
    ImFontAtlasBuildUpdateLinesTexData(atlas);
4065
0
    ImFontAtlasBuildUpdateBasicTexData(atlas);
4066
4067
0
    builder->LockDisableResize = false;
4068
0
    ImFontAtlasUpdateDrawListsSharedData(atlas);
4069
    //ImFontAtlasDebugWriteTexToDisk(new_tex, "After Pack");
4070
0
}
4071
4072
void ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_tex_w, int old_tex_h)
4073
0
{
4074
    //ImFontAtlasDebugWriteTexToDisk(atlas->TexData, "Before Grow");
4075
0
    ImFontAtlasBuilder* builder = atlas->Builder;
4076
0
    if (old_tex_w == -1)
4077
0
        old_tex_w = atlas->TexData->Width;
4078
0
    if (old_tex_h == -1)
4079
0
        old_tex_h = atlas->TexData->Height;
4080
4081
    // FIXME-NEWATLAS-V2: What to do when reaching limits exposed by backend?
4082
    // FIXME-NEWATLAS-V2: Does ImFontAtlasFlags_NoPowerOfTwoHeight makes sense now? Allow 'lock' and 'compact' operations?
4083
0
    IM_ASSERT(ImIsPowerOfTwo(old_tex_w) && ImIsPowerOfTwo(old_tex_h));
4084
0
    IM_ASSERT(ImIsPowerOfTwo(atlas->TexMinWidth) && ImIsPowerOfTwo(atlas->TexMaxWidth) && ImIsPowerOfTwo(atlas->TexMinHeight) && ImIsPowerOfTwo(atlas->TexMaxHeight));
4085
4086
    // Grow texture so it follows roughly a square.
4087
    // - Grow height before width, as width imply more packing nodes.
4088
    // - Caller should be taking account of RectsDiscardedSurface and may not need to grow.
4089
0
    int new_tex_w = (old_tex_h <= old_tex_w) ? old_tex_w : old_tex_w * 2;
4090
0
    int new_tex_h = (old_tex_h <= old_tex_w) ? old_tex_h * 2 : old_tex_h;
4091
4092
    // Handle minimum size first (for pathologically large packed rects)
4093
0
    const int pack_padding = atlas->TexGlyphPadding;
4094
0
    new_tex_w = ImMax(new_tex_w, ImUpperPowerOfTwo(builder->MaxRectSize.x + pack_padding));
4095
0
    new_tex_h = ImMax(new_tex_h, ImUpperPowerOfTwo(builder->MaxRectSize.y + pack_padding));
4096
0
    new_tex_w = ImClamp(new_tex_w, atlas->TexMinWidth, atlas->TexMaxWidth);
4097
0
    new_tex_h = ImClamp(new_tex_h, atlas->TexMinHeight, atlas->TexMaxHeight);
4098
0
    if (new_tex_w == old_tex_w && new_tex_h == old_tex_h)
4099
0
        return;
4100
4101
0
    ImFontAtlasTextureRepack(atlas, new_tex_w, new_tex_h);
4102
0
}
4103
4104
void ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas)
4105
0
{
4106
    // Can some baked contents be ditched?
4107
    //IMGUI_DEBUG_LOG_FONT("[font] ImFontAtlasBuildMakeSpace()\n");
4108
0
    ImFontAtlasBuilder* builder = atlas->Builder;
4109
0
    ImFontAtlasBuildDiscardBakes(atlas, 2);
4110
4111
    // Currently using a heuristic for repack without growing.
4112
0
    if (builder->RectsDiscardedSurface < builder->RectsPackedSurface * 0.20f)
4113
0
        ImFontAtlasTextureGrow(atlas);
4114
0
    else
4115
0
        ImFontAtlasTextureRepack(atlas, atlas->TexData->Width, atlas->TexData->Height);
4116
0
}
4117
4118
ImVec2i ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas)
4119
0
{
4120
0
    int min_w = ImUpperPowerOfTwo(atlas->TexMinWidth);
4121
0
    int min_h = ImUpperPowerOfTwo(atlas->TexMinHeight);
4122
0
    if (atlas->Builder == NULL || atlas->TexData == NULL || atlas->TexData->Status == ImTextureStatus_WantDestroy)
4123
0
        return ImVec2i(min_w, min_h);
4124
4125
0
    ImFontAtlasBuilder* builder = atlas->Builder;
4126
0
    min_w = ImMax(ImUpperPowerOfTwo(builder->MaxRectSize.x), min_w);
4127
0
    min_h = ImMax(ImUpperPowerOfTwo(builder->MaxRectSize.y), min_h);
4128
0
    const int surface_approx = builder->RectsPackedSurface - builder->RectsDiscardedSurface; // Expected surface after repack
4129
0
    const int surface_sqrt = (int)sqrtf((float)surface_approx);
4130
4131
0
    int new_tex_w;
4132
0
    int new_tex_h;
4133
0
    if (min_w >= min_h)
4134
0
    {
4135
0
        new_tex_w = ImMax(min_w, ImUpperPowerOfTwo(surface_sqrt));
4136
0
        new_tex_h = ImMax(min_h, (int)((surface_approx + new_tex_w - 1) / new_tex_w));
4137
0
        if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0)
4138
0
            new_tex_h = ImUpperPowerOfTwo(new_tex_h);
4139
0
    }
4140
0
    else
4141
0
    {
4142
0
        new_tex_h = ImMax(min_h, ImUpperPowerOfTwo(surface_sqrt));
4143
0
        if ((atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) == 0)
4144
0
            new_tex_h = ImUpperPowerOfTwo(new_tex_h);
4145
0
        new_tex_w = ImMax(min_w, (int)((surface_approx + new_tex_h - 1) / new_tex_h));
4146
0
    }
4147
4148
0
    IM_ASSERT(ImIsPowerOfTwo(new_tex_w) && ImIsPowerOfTwo(new_tex_h));
4149
0
    return ImVec2i(new_tex_w, new_tex_h);
4150
0
}
4151
4152
// Clear all output. Invalidates all AddCustomRect() return values!
4153
void ImFontAtlasBuildClear(ImFontAtlas* atlas)
4154
0
{
4155
0
    ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas);
4156
0
    ImFontAtlasBuildDestroy(atlas);
4157
0
    ImFontAtlasTextureAdd(atlas, new_tex_size.x, new_tex_size.y);
4158
0
    ImFontAtlasBuildInit(atlas);
4159
0
    for (ImFontConfig& src : atlas->Sources)
4160
0
        ImFontAtlasFontSourceInit(atlas, &src);
4161
0
    for (ImFont* font : atlas->Fonts)
4162
0
        for (ImFontConfig* src : font->Sources)
4163
0
            ImFontAtlasFontSourceAddToFont(atlas, font, src);
4164
0
}
4165
4166
// You should not need to call this manually!
4167
// If you think you do, let us know and we can advise about policies auto-compact.
4168
void ImFontAtlasTextureCompact(ImFontAtlas* atlas)
4169
0
{
4170
0
    ImFontAtlasBuilder* builder = atlas->Builder;
4171
0
    ImFontAtlasBuildDiscardBakes(atlas, 1);
4172
4173
0
    ImTextureData* old_tex = atlas->TexData;
4174
0
    ImVec2i old_tex_size = ImVec2i(old_tex->Width, old_tex->Height);
4175
0
    ImVec2i new_tex_size = ImFontAtlasTextureGetSizeEstimate(atlas);
4176
0
    if (builder->RectsDiscardedCount == 0 && new_tex_size.x == old_tex_size.x && new_tex_size.y == old_tex_size.y)
4177
0
        return;
4178
4179
0
    ImFontAtlasTextureRepack(atlas, new_tex_size.x, new_tex_size.y);
4180
0
}
4181
4182
// Start packing over current empty texture
4183
void ImFontAtlasBuildInit(ImFontAtlas* atlas)
4184
1
{
4185
    // Select Backend
4186
    // - Note that we do not reassign to atlas->FontLoader, since it is likely to point to static data which
4187
    //   may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are
4188
    //   using a hot-reloading scheme that messes up static data, store your own instance of FontLoader somewhere
4189
    //   and point to it instead of pointing directly to return value of the GetFontLoaderXXX functions.
4190
1
    if (atlas->FontLoader == NULL)
4191
1
    {
4192
#ifdef IMGUI_ENABLE_FREETYPE
4193
        atlas->SetFontLoader(ImGuiFreeType::GetFontLoader());
4194
#elif defined(IMGUI_ENABLE_STB_TRUETYPE)
4195
        atlas->SetFontLoader(ImFontAtlasGetFontLoaderForStbTruetype());
4196
#else
4197
        IM_ASSERT(0); // Invalid Build function
4198
#endif
4199
1
    }
4200
4201
    // Create initial texture size
4202
1
    if (atlas->TexData == NULL || atlas->TexData->Pixels == NULL)
4203
1
        ImFontAtlasTextureAdd(atlas, ImUpperPowerOfTwo(atlas->TexMinWidth), ImUpperPowerOfTwo(atlas->TexMinHeight));
4204
4205
1
    atlas->Builder = IM_NEW(ImFontAtlasBuilder)();
4206
1
    if (atlas->FontLoader->LoaderInit)
4207
0
        atlas->FontLoader->LoaderInit(atlas);
4208
4209
1
    ImFontAtlasBuildUpdateRendererHasTexturesFromContext(atlas);
4210
4211
1
    ImFontAtlasPackInit(atlas);
4212
4213
    // Add required texture data
4214
1
    ImFontAtlasBuildUpdateLinesTexData(atlas);
4215
1
    ImFontAtlasBuildUpdateBasicTexData(atlas);
4216
4217
    // Register fonts
4218
1
    ImFontAtlasBuildUpdatePointers(atlas);
4219
4220
    // Update UV coordinates etc. stored in bound ImDrawListSharedData instance
4221
1
    ImFontAtlasUpdateDrawListsSharedData(atlas);
4222
4223
    //atlas->TexIsBuilt = true;
4224
1
}
4225
4226
// Destroy builder and all cached glyphs. Do not destroy actual fonts.
4227
void ImFontAtlasBuildDestroy(ImFontAtlas* atlas)
4228
1
{
4229
1
    for (ImFont* font : atlas->Fonts)
4230
1
        ImFontAtlasFontDestroyOutput(atlas, font);
4231
1
    if (atlas->Builder && atlas->FontLoader && atlas->FontLoader->LoaderShutdown)
4232
0
    {
4233
0
        atlas->FontLoader->LoaderShutdown(atlas);
4234
0
        IM_ASSERT(atlas->FontLoaderData == NULL);
4235
0
    }
4236
1
    IM_DELETE(atlas->Builder);
4237
1
    atlas->Builder = NULL;
4238
1
}
4239
4240
void ImFontAtlasPackInit(ImFontAtlas * atlas)
4241
1
{
4242
1
    ImTextureData* tex = atlas->TexData;
4243
1
    ImFontAtlasBuilder* builder = atlas->Builder;
4244
4245
    // In theory we could decide to reduce the number of nodes, e.g. halve them, and waste a little texture space, but it doesn't seem worth it.
4246
1
    const int pack_node_count = tex->Width / 2;
4247
1
    builder->PackNodes.resize(pack_node_count);
4248
1
    IM_STATIC_ASSERT(sizeof(stbrp_context) <= sizeof(stbrp_context_opaque));
4249
1
    stbrp_init_target((stbrp_context*)(void*)&builder->PackContext, tex->Width, tex->Height, builder->PackNodes.Data, builder->PackNodes.Size);
4250
1
    builder->RectsPackedSurface = builder->RectsPackedCount = 0;
4251
1
    builder->MaxRectSize = ImVec2i(0, 0);
4252
1
    builder->MaxRectBounds = ImVec2i(0, 0);
4253
1
}
4254
4255
// This is essentially a free-list pattern, it may be nice to wrap it into a dedicated type.
4256
static ImFontAtlasRectId ImFontAtlasPackAllocRectEntry(ImFontAtlas* atlas, int rect_idx)
4257
22
{
4258
22
    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
4259
22
    int index_idx;
4260
22
    ImFontAtlasRectEntry* index_entry;
4261
22
    if (builder->RectsIndexFreeListStart < 0)
4262
22
    {
4263
22
        builder->RectsIndex.resize(builder->RectsIndex.Size + 1);
4264
22
        index_idx = builder->RectsIndex.Size - 1;
4265
22
        index_entry = &builder->RectsIndex[index_idx];
4266
22
        memset(index_entry, 0, sizeof(*index_entry));
4267
22
    }
4268
0
    else
4269
0
    {
4270
0
        index_idx = builder->RectsIndexFreeListStart;
4271
0
        index_entry = &builder->RectsIndex[index_idx];
4272
0
        IM_ASSERT(index_entry->IsUsed == false && index_entry->Generation > 0); // Generation is incremented during DiscardRect
4273
0
        builder->RectsIndexFreeListStart = index_entry->TargetIndex;
4274
0
    }
4275
22
    index_entry->TargetIndex = rect_idx;
4276
22
    index_entry->IsUsed = 1;
4277
22
    return ImFontAtlasRectId_Make(index_idx, index_entry->Generation);
4278
22
}
4279
4280
// Overwrite existing entry
4281
static ImFontAtlasRectId ImFontAtlasPackReuseRectEntry(ImFontAtlas* atlas, ImFontAtlasRectEntry* index_entry)
4282
0
{
4283
0
    IM_ASSERT(index_entry->IsUsed);
4284
0
    index_entry->TargetIndex = atlas->Builder->Rects.Size - 1;
4285
0
    int index_idx = atlas->Builder->RectsIndex.index_from_ptr(index_entry);
4286
0
    return ImFontAtlasRectId_Make(index_idx, index_entry->Generation);
4287
0
}
4288
4289
// This is expected to be called in batches and followed by a repack
4290
void ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id)
4291
20
{
4292
20
    IM_ASSERT(id != ImFontAtlasRectId_Invalid);
4293
4294
20
    ImTextureRect* rect = ImFontAtlasPackGetRect(atlas, id);
4295
20
    if (rect == NULL)
4296
0
        return;
4297
4298
20
    ImFontAtlasBuilder* builder = atlas->Builder;
4299
20
    int index_idx = ImFontAtlasRectId_GetIndex(id);
4300
20
    ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
4301
20
    IM_ASSERT(index_entry->IsUsed && index_entry->TargetIndex >= 0);
4302
20
    index_entry->IsUsed = false;
4303
20
    index_entry->TargetIndex = builder->RectsIndexFreeListStart;
4304
20
    index_entry->Generation++;
4305
20
    if (index_entry->Generation == 0)
4306
0
        index_entry->Generation++; // Keep non-zero on overflow
4307
4308
20
    const int pack_padding = atlas->TexGlyphPadding;
4309
20
    builder->RectsIndexFreeListStart = index_idx;
4310
20
    builder->RectsDiscardedCount++;
4311
20
    builder->RectsDiscardedSurface += (rect->w + pack_padding) * (rect->h + pack_padding);
4312
20
    rect->w = rect->h = 0; // Clear rectangle so it won't be packed again
4313
20
}
4314
4315
// Important: Calling this may recreate a new texture and therefore change atlas->TexData
4316
// FIXME-NEWFONTS: Expose other glyph padding settings for custom alteration (e.g. drop shadows). See #7962
4317
ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry)
4318
22
{
4319
22
    IM_ASSERT(w > 0 && w <= 0xFFFF);
4320
22
    IM_ASSERT(h > 0 && h <= 0xFFFF);
4321
4322
22
    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
4323
22
    const int pack_padding = atlas->TexGlyphPadding;
4324
22
    builder->MaxRectSize.x = ImMax(builder->MaxRectSize.x, w);
4325
22
    builder->MaxRectSize.y = ImMax(builder->MaxRectSize.y, h);
4326
4327
    // Pack
4328
22
    ImTextureRect r = { 0, 0, (unsigned short)w, (unsigned short)h };
4329
22
    for (int attempts_remaining = 3; attempts_remaining >= 0; attempts_remaining--)
4330
22
    {
4331
        // Try packing
4332
22
        stbrp_rect pack_r = {};
4333
22
        pack_r.w = w + pack_padding;
4334
22
        pack_r.h = h + pack_padding;
4335
22
        stbrp_pack_rects((stbrp_context*)(void*)&builder->PackContext, &pack_r, 1);
4336
22
        r.x = (unsigned short)pack_r.x;
4337
22
        r.y = (unsigned short)pack_r.y;
4338
22
        if (pack_r.was_packed)
4339
22
            break;
4340
4341
        // If we ran out of attempts, return fallback
4342
0
        if (attempts_remaining == 0 || builder->LockDisableResize)
4343
0
        {
4344
0
            IMGUI_DEBUG_LOG_FONT("[font] Failed packing %dx%d rectangle. Returning fallback.\n", w, h);
4345
0
            return ImFontAtlasRectId_Invalid;
4346
0
        }
4347
4348
        // Resize or repack atlas! (this should be a rare event)
4349
0
        ImFontAtlasTextureMakeSpace(atlas);
4350
0
    }
4351
4352
22
    builder->MaxRectBounds.x = ImMax(builder->MaxRectBounds.x, r.x + r.w + pack_padding);
4353
22
    builder->MaxRectBounds.y = ImMax(builder->MaxRectBounds.y, r.y + r.h + pack_padding);
4354
22
    builder->RectsPackedCount++;
4355
22
    builder->RectsPackedSurface += (w + pack_padding) * (h + pack_padding);
4356
4357
22
    builder->Rects.push_back(r);
4358
22
    if (overwrite_entry != NULL)
4359
0
        return ImFontAtlasPackReuseRectEntry(atlas, overwrite_entry); // Write into an existing entry instead of adding one (used during repack)
4360
22
    else
4361
22
        return ImFontAtlasPackAllocRectEntry(atlas, builder->Rects.Size - 1);
4362
22
}
4363
4364
// Generally for non-user facing functions: assert on invalid ID.
4365
ImTextureRect* ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id)
4366
62
{
4367
62
    IM_ASSERT(id != ImFontAtlasRectId_Invalid);
4368
62
    int index_idx = ImFontAtlasRectId_GetIndex(id);
4369
62
    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
4370
62
    ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
4371
62
    IM_ASSERT(index_entry->Generation == ImFontAtlasRectId_GetGeneration(id));
4372
62
    IM_ASSERT(index_entry->IsUsed);
4373
62
    return &builder->Rects[index_entry->TargetIndex];
4374
62
}
4375
4376
// For user-facing functions: return NULL on invalid ID.
4377
// Important: return pointer is valid until next call to AddRect(), e.g. FindGlyph(), CalcTextSize() can all potentially invalidate previous pointers.
4378
ImTextureRect* ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id)
4379
4
{
4380
4
    if (id == ImFontAtlasRectId_Invalid)
4381
2
        return NULL;
4382
2
    int index_idx = ImFontAtlasRectId_GetIndex(id);
4383
2
    if (atlas->Builder == NULL)
4384
0
        ImFontAtlasBuildInit(atlas);
4385
2
    ImFontAtlasBuilder* builder = (ImFontAtlasBuilder*)atlas->Builder;
4386
2
    IM_MSVC_WARNING_SUPPRESS(28182); // Static Analysis false positive "warning C28182: Dereferencing NULL pointer 'builder'"
4387
2
    if (index_idx >= builder->RectsIndex.Size)
4388
0
        return NULL;
4389
2
    ImFontAtlasRectEntry* index_entry = &builder->RectsIndex[index_idx];
4390
2
    if (index_entry->Generation != ImFontAtlasRectId_GetGeneration(id) || !index_entry->IsUsed)
4391
0
        return NULL;
4392
2
    return &builder->Rects[index_entry->TargetIndex];
4393
2
}
4394
4395
// Important! This assume by ImFontConfig::GlyphExcludeRanges[] is a SMALL ARRAY (e.g. <10 entries)
4396
// Use "Input Glyphs Overlap Detection Tool" to display a list of glyphs provided by multiple sources in order to set this array up.
4397
static bool ImFontAtlasBuildAcceptCodepointForSource(ImFontConfig* src, ImWchar codepoint)
4398
0
{
4399
0
    if (const ImWchar* exclude_list = src->GlyphExcludeRanges)
4400
0
        for (; exclude_list[0] != 0; exclude_list += 2)
4401
0
            if (codepoint >= exclude_list[0] && codepoint <= exclude_list[1])
4402
0
                return false;
4403
0
    return true;
4404
0
}
4405
4406
static void ImFontBaked_BuildGrowIndex(ImFontBaked* baked, int new_size)
4407
22
{
4408
22
    IM_ASSERT(baked->IndexAdvanceX.Size == baked->IndexLookup.Size);
4409
22
    if (new_size <= baked->IndexLookup.Size)
4410
16
        return;
4411
6
    baked->IndexAdvanceX.resize(new_size, -1.0f);
4412
6
    baked->IndexLookup.resize(new_size, IM_FONTGLYPH_INDEX_UNUSED);
4413
6
}
4414
4415
static void ImFontAtlas_FontHookRemapCodepoint(ImFontAtlas* atlas, ImFont* font, ImWchar* c)
4416
25
{
4417
25
    IM_UNUSED(atlas);
4418
25
    if (font->RemapPairs.Data.Size != 0)
4419
0
        *c = (ImWchar)font->RemapPairs.GetInt((ImGuiID)*c, (int)*c);
4420
25
}
4421
4422
static ImFontGlyph* ImFontBaked_BuildLoadGlyph(ImFontBaked* baked, ImWchar codepoint, float* only_load_advance_x)
4423
22
{
4424
22
    ImFont* font = baked->ContainerFont;
4425
22
    ImFontAtlas* atlas = font->ContainerAtlas;
4426
22
    if (atlas->Locked || (font->Flags & ImFontFlags_NoLoadGlyphs))
4427
0
    {
4428
        // Lazily load fallback glyph
4429
0
        if (baked->FallbackGlyphIndex == -1 && baked->LoadNoFallback == 0)
4430
0
            ImFontAtlasBuildSetupFontBakedFallback(baked);
4431
0
        return NULL;
4432
0
    }
4433
4434
    // User remapping hooks
4435
22
    ImWchar src_codepoint = codepoint;
4436
22
    ImFontAtlas_FontHookRemapCodepoint(atlas, font, &codepoint);
4437
4438
    //char utf8_buf[5];
4439
    //IMGUI_DEBUG_LOG("[font] BuildLoadGlyph U+%04X (%s)\n", (unsigned int)codepoint, ImTextCharToUtf8(utf8_buf, (unsigned int)codepoint));
4440
4441
    // Special hook
4442
    // FIXME-NEWATLAS: it would be nicer if this used a more standardized way of hooking
4443
22
    if (codepoint == font->EllipsisChar && font->EllipsisAutoBake)
4444
0
        if (ImFontGlyph* glyph = ImFontAtlasBuildSetupFontBakedEllipsis(atlas, baked))
4445
0
            return glyph;
4446
4447
    // Call backend
4448
22
    char* loader_user_data_p = (char*)baked->FontLoaderDatas;
4449
22
    int src_n = 0;
4450
22
    for (ImFontConfig* src : font->Sources)
4451
22
    {
4452
22
        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
4453
22
        if (!src->GlyphExcludeRanges || ImFontAtlasBuildAcceptCodepointForSource(src, codepoint))
4454
22
        {
4455
22
            if (only_load_advance_x == NULL)
4456
22
            {
4457
22
                ImFontGlyph glyph_buf;
4458
22
                if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, &glyph_buf, NULL))
4459
22
                {
4460
                    // FIXME: Add hooks for e.g. #7962
4461
22
                    glyph_buf.Codepoint = src_codepoint;
4462
22
                    glyph_buf.SourceIdx = src_n;
4463
22
                    return ImFontAtlasBakedAddFontGlyph(atlas, baked, src, &glyph_buf);
4464
22
                }
4465
22
            }
4466
0
            else
4467
0
            {
4468
                // Special mode but only loading glyphs metrics. Will rasterize and pack later.
4469
0
                if (loader->FontBakedLoadGlyph(atlas, src, baked, loader_user_data_p, codepoint, NULL, only_load_advance_x))
4470
0
                {
4471
0
                    ImFontAtlasBakedAddFontGlyphAdvancedX(atlas, baked, src, codepoint, *only_load_advance_x);
4472
0
                    return NULL;
4473
0
                }
4474
0
            }
4475
22
        }
4476
0
        loader_user_data_p += loader->FontBakedSrcLoaderDataSize;
4477
0
        src_n++;
4478
0
    }
4479
4480
    // Lazily load fallback glyph
4481
0
    if (baked->LoadNoFallback)
4482
0
        return NULL;
4483
0
    if (baked->FallbackGlyphIndex == -1)
4484
0
        ImFontAtlasBuildSetupFontBakedFallback(baked);
4485
4486
    // Mark index as not found, so we don't attempt the search twice
4487
0
    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
4488
0
    baked->IndexAdvanceX[codepoint] = baked->FallbackAdvanceX;
4489
0
    baked->IndexLookup[codepoint] = IM_FONTGLYPH_INDEX_NOT_FOUND;
4490
0
    return NULL;
4491
0
}
4492
4493
static float ImFontBaked_BuildLoadGlyphAdvanceX(ImFontBaked* baked, ImWchar codepoint)
4494
20
{
4495
20
    if (baked->Size >= IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE || baked->LoadNoRenderOnLayout)
4496
0
    {
4497
        // First load AdvanceX value used by CalcTextSize() API then load the rest when loaded by drawing API.
4498
0
        float only_advance_x = 0.0f;
4499
0
        ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, &only_advance_x);
4500
0
        return glyph ? glyph->AdvanceX : only_advance_x;
4501
0
    }
4502
20
    else
4503
20
    {
4504
20
        ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(baked, (ImWchar)codepoint, NULL);
4505
20
        return glyph ? glyph->AdvanceX : baked->FallbackAdvanceX;
4506
20
    }
4507
20
}
4508
4509
// The point of this indirection is to not be inlined in debug mode in order to not bloat inner loop.b
4510
IM_MSVC_RUNTIME_CHECKS_OFF
4511
static float BuildLoadGlyphGetAdvanceOrFallback(ImFontBaked* baked, unsigned int codepoint)
4512
20
{
4513
20
    return ImFontBaked_BuildLoadGlyphAdvanceX(baked, (ImWchar)codepoint);
4514
20
}
4515
IM_MSVC_RUNTIME_CHECKS_RESTORE
4516
4517
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4518
void ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas)
4519
80.5k
{
4520
    // [DEBUG] Log texture update requests
4521
80.5k
    ImGuiContext& g = *GImGui;
4522
80.5k
    IM_UNUSED(g);
4523
80.5k
    for (ImTextureData* tex : atlas->TexList)
4524
80.5k
    {
4525
80.5k
        if ((g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) == 0)
4526
80.5k
            IM_ASSERT(tex->Updates.Size == 0);
4527
80.5k
        if (tex->Status == ImTextureStatus_WantCreate)
4528
1
            IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: create %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
4529
80.5k
        else if (tex->Status == ImTextureStatus_WantDestroy)
4530
0
            IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: destroy %dx%d, texid=0x%" IM_PRIX64 ", backend_data=%p\n", tex->UniqueID, tex->Width, tex->Height, IM_TEXTUREID_TO_U64(tex->TexID), tex->BackendUserData);
4531
80.5k
        else if (tex->Status == ImTextureStatus_WantUpdates)
4532
1
        {
4533
1
            IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update %d regions, texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, tex->Updates.Size, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);
4534
1
            for (const ImTextureRect& r : tex->Updates)
4535
4
            {
4536
4
                IM_UNUSED(r);
4537
4
                IM_ASSERT(r.x >= 0 && r.y >= 0);
4538
4
                IM_ASSERT(r.x + r.w <= tex->Width && r.y + r.h <= tex->Height); // In theory should subtract PackPadding but it's currently part of atlas and mid-frame change would wreck assert.
4539
                //IMGUI_DEBUG_LOG_FONT("[font] Texture #%03d: update (% 4d..%-4d)->(% 4d..%-4d), texid=0x%" IM_PRIX64 ", backend_data=0x%" IM_PRIX64 "\n", tex->UniqueID, r.x, r.y, r.x + r.w, r.y + r.h, IM_TEXTUREID_TO_U64(tex->TexID), (ImU64)(intptr_t)tex->BackendUserData);
4540
4
            }
4541
1
        }
4542
80.5k
    }
4543
80.5k
}
4544
#endif
4545
4546
//-------------------------------------------------------------------------
4547
// [SECTION] ImFontAtlas: backend for stb_truetype
4548
//-------------------------------------------------------------------------
4549
// (imstb_truetype.h in included near the top of this file, when IMGUI_ENABLE_STB_TRUETYPE is set)
4550
//-------------------------------------------------------------------------
4551
4552
#ifdef IMGUI_ENABLE_STB_TRUETYPE
4553
4554
// One for each ConfigData
4555
struct ImGui_ImplStbTrueType_FontSrcData
4556
{
4557
    stbtt_fontinfo  FontInfo;
4558
    float           ScaleFactor;
4559
};
4560
4561
static bool ImGui_ImplStbTrueType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src)
4562
1
{
4563
1
    IM_UNUSED(atlas);
4564
4565
1
    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplStbTrueType_FontSrcData);
4566
1
    IM_ASSERT(src->FontLoaderData == NULL);
4567
4568
    // Initialize helper structure for font loading and verify that the TTF/OTF data is correct
4569
1
    const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)src->FontData, src->FontNo);
4570
1
    if (font_offset < 0)
4571
0
    {
4572
0
        IM_DELETE(bd_font_data);
4573
0
        IM_ASSERT_USER_ERROR(0, "stbtt_GetFontOffsetForIndex(): FontData is incorrect, or FontNo cannot be found.");
4574
0
        return false;
4575
0
    }
4576
1
    if (!stbtt_InitFont(&bd_font_data->FontInfo, (unsigned char*)src->FontData, font_offset))
4577
0
    {
4578
0
        IM_DELETE(bd_font_data);
4579
0
        IM_ASSERT_USER_ERROR(0, "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.");
4580
0
        return false;
4581
0
    }
4582
1
    src->FontLoaderData = bd_font_data;
4583
4584
1
    const float ref_size = src->DstFont->Sources[0]->SizePixels;
4585
1
    if (src->MergeMode && src->SizePixels == 0.0f)
4586
0
        src->SizePixels = ref_size;
4587
4588
1
    if (src->SizePixels >= 0.0f)
4589
1
        bd_font_data->ScaleFactor = stbtt_ScaleForPixelHeight(&bd_font_data->FontInfo, 1.0f);
4590
0
    else
4591
0
        bd_font_data->ScaleFactor = stbtt_ScaleForMappingEmToPixels(&bd_font_data->FontInfo, 1.0f);
4592
1
    if (src->MergeMode && src->SizePixels != 0.0f && ref_size != 0.0f)
4593
0
        bd_font_data->ScaleFactor *= src->SizePixels / ref_size; // FIXME-NEWATLAS: Should tidy up that a bit
4594
4595
1
    return true;
4596
1
}
4597
4598
static void ImGui_ImplStbTrueType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src)
4599
2
{
4600
2
    IM_UNUSED(atlas);
4601
2
    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
4602
2
    IM_DELETE(bd_font_data);
4603
2
    src->FontLoaderData = NULL;
4604
2
}
4605
4606
static bool ImGui_ImplStbTrueType_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint)
4607
3
{
4608
3
    IM_UNUSED(atlas);
4609
4610
3
    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
4611
3
    IM_ASSERT(bd_font_data != NULL);
4612
4613
3
    int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint);
4614
3
    return glyph_index != 0;
4615
3
}
4616
4617
static bool ImGui_ImplStbTrueType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*)
4618
1
{
4619
1
    IM_UNUSED(atlas);
4620
4621
1
    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
4622
1
    if (src->MergeMode == false)
4623
1
    {
4624
        // FIXME-NEWFONTS: reevaluate how to use sizing metrics
4625
        // FIXME-NEWFONTS: make use of line gap value
4626
1
        float scale_for_layout = bd_font_data->ScaleFactor * baked->Size;
4627
1
        int unscaled_ascent, unscaled_descent, unscaled_line_gap;
4628
1
        stbtt_GetFontVMetrics(&bd_font_data->FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
4629
1
        baked->Ascent = ImCeil(unscaled_ascent * scale_for_layout);
4630
1
        baked->Descent = ImFloor(unscaled_descent * scale_for_layout);
4631
1
    }
4632
1
    return true;
4633
1
}
4634
4635
static bool ImGui_ImplStbTrueType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void*, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x)
4636
22
{
4637
    // Search for first font which has the glyph
4638
22
    ImGui_ImplStbTrueType_FontSrcData* bd_font_data = (ImGui_ImplStbTrueType_FontSrcData*)src->FontLoaderData;
4639
22
    IM_ASSERT(bd_font_data);
4640
22
    int glyph_index = stbtt_FindGlyphIndex(&bd_font_data->FontInfo, (int)codepoint);
4641
22
    if (glyph_index == 0)
4642
0
        return false;
4643
4644
    // Fonts unit to pixels
4645
22
    int oversample_h, oversample_v;
4646
22
    ImFontAtlasBuildGetOversampleFactors(src, baked, &oversample_h, &oversample_v);
4647
22
    const float scale_for_layout = bd_font_data->ScaleFactor * baked->Size;
4648
22
    const float rasterizer_density = src->RasterizerDensity * baked->RasterizerDensity;
4649
22
    const float scale_for_raster_x = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_h;
4650
22
    const float scale_for_raster_y = bd_font_data->ScaleFactor * baked->Size * rasterizer_density * oversample_v;
4651
4652
    // Obtain size and advance
4653
22
    int x0, y0, x1, y1;
4654
22
    int advance, lsb;
4655
22
    stbtt_GetGlyphBitmapBoxSubpixel(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, 0, 0, &x0, &y0, &x1, &y1);
4656
22
    stbtt_GetGlyphHMetrics(&bd_font_data->FontInfo, glyph_index, &advance, &lsb);
4657
4658
    // Load metrics only mode
4659
22
    if (out_advance_x != NULL)
4660
0
    {
4661
0
        IM_ASSERT(out_glyph == NULL);
4662
0
        *out_advance_x = advance * scale_for_layout;
4663
0
        return true;
4664
0
    }
4665
4666
    // Prepare glyph
4667
22
    out_glyph->Codepoint = codepoint;
4668
22
    out_glyph->AdvanceX = advance * scale_for_layout;
4669
4670
    // Pack and retrieve position inside texture atlas
4671
    // (generally based on stbtt_PackFontRangesRenderIntoRects)
4672
22
    const bool is_visible = (x0 != x1 && y0 != y1);
4673
22
    if (is_visible)
4674
20
    {
4675
20
        const int w = (x1 - x0 + oversample_h - 1);
4676
20
        const int h = (y1 - y0 + oversample_v - 1);
4677
20
        ImFontAtlasRectId pack_id = ImFontAtlasPackAddRect(atlas, w, h);
4678
20
        if (pack_id == ImFontAtlasRectId_Invalid)
4679
0
        {
4680
            // Pathological out of memory case (TexMaxWidth/TexMaxHeight set too small?)
4681
0
            IM_ASSERT(pack_id != ImFontAtlasRectId_Invalid && "Out of texture memory.");
4682
0
            return false;
4683
0
        }
4684
20
        ImTextureRect* r = ImFontAtlasPackGetRect(atlas, pack_id);
4685
4686
        // Render
4687
20
        stbtt_GetGlyphBitmapBox(&bd_font_data->FontInfo, glyph_index, scale_for_raster_x, scale_for_raster_y, &x0, &y0, &x1, &y1);
4688
20
        ImFontAtlasBuilder* builder = atlas->Builder;
4689
20
        builder->TempBuffer.resize(w * h * 1);
4690
20
        unsigned char* bitmap_pixels = builder->TempBuffer.Data;
4691
20
        memset(bitmap_pixels, 0, w * h * 1);
4692
4693
        // Render with oversampling
4694
        // (those functions conveniently assert if pixels are not cleared, which is another safety layer)
4695
20
        float sub_x, sub_y;
4696
20
        stbtt_MakeGlyphBitmapSubpixelPrefilter(&bd_font_data->FontInfo, bitmap_pixels, w, h, w,
4697
20
            scale_for_raster_x, scale_for_raster_y, 0, 0, oversample_h, oversample_v, &sub_x, &sub_y, glyph_index);
4698
4699
20
        const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
4700
20
        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
4701
20
        float font_off_x = (src->GlyphOffset.x * offsets_scale);
4702
20
        float font_off_y = (src->GlyphOffset.y * offsets_scale);
4703
20
        if (src->PixelSnapH) // Snap scaled offset. This is to mitigate backward compatibility issues for GlyphOffset, but a better design would be welcome.
4704
20
            font_off_x = IM_ROUND(font_off_x);
4705
20
        if (src->PixelSnapV)
4706
0
            font_off_y = IM_ROUND(font_off_y);
4707
20
        font_off_x += sub_x;
4708
20
        font_off_y += sub_y + IM_ROUND(baked->Ascent);
4709
20
        float recip_h = 1.0f / (oversample_h * rasterizer_density);
4710
20
        float recip_v = 1.0f / (oversample_v * rasterizer_density);
4711
4712
        // Register glyph
4713
        // r->x r->y are coordinates inside texture (in pixels)
4714
        // glyph.X0, glyph.Y0 are drawing coordinates from base text position, and accounting for oversampling.
4715
20
        out_glyph->X0 = x0 * recip_h + font_off_x;
4716
20
        out_glyph->Y0 = y0 * recip_v + font_off_y;
4717
20
        out_glyph->X1 = (x0 + (int)r->w) * recip_h + font_off_x;
4718
20
        out_glyph->Y1 = (y0 + (int)r->h) * recip_v + font_off_y;
4719
20
        out_glyph->Visible = true;
4720
20
        out_glyph->PackId = pack_id;
4721
20
        ImFontAtlasBakedSetFontGlyphBitmap(atlas, baked, src, out_glyph, r, bitmap_pixels, ImTextureFormat_Alpha8, w);
4722
20
    }
4723
4724
22
    return true;
4725
22
}
4726
4727
const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype()
4728
1
{
4729
1
    static ImFontLoader loader;
4730
1
    loader.Name = "stb_truetype";
4731
1
    loader.FontSrcInit = ImGui_ImplStbTrueType_FontSrcInit;
4732
1
    loader.FontSrcDestroy = ImGui_ImplStbTrueType_FontSrcDestroy;
4733
1
    loader.FontSrcContainsGlyph = ImGui_ImplStbTrueType_FontSrcContainsGlyph;
4734
1
    loader.FontBakedInit = ImGui_ImplStbTrueType_FontBakedInit;
4735
1
    loader.FontBakedDestroy = NULL;
4736
1
    loader.FontBakedLoadGlyph = ImGui_ImplStbTrueType_FontBakedLoadGlyph;
4737
1
    return &loader;
4738
1
}
4739
4740
#endif // IMGUI_ENABLE_STB_TRUETYPE
4741
4742
//-------------------------------------------------------------------------
4743
// [SECTION] ImFontAtlas: glyph ranges helpers
4744
//-------------------------------------------------------------------------
4745
// - GetGlyphRangesDefault()
4746
// Obsolete functions since 1.92:
4747
// - GetGlyphRangesGreek()
4748
// - GetGlyphRangesKorean()
4749
// - GetGlyphRangesChineseFull()
4750
// - GetGlyphRangesChineseSimplifiedCommon()
4751
// - GetGlyphRangesJapanese()
4752
// - GetGlyphRangesCyrillic()
4753
// - GetGlyphRangesThai()
4754
// - GetGlyphRangesVietnamese()
4755
//-----------------------------------------------------------------------------
4756
4757
// Retrieve list of range (2 int per range, values are inclusive)
4758
const ImWchar*   ImFontAtlas::GetGlyphRangesDefault()
4759
0
{
4760
0
    static const ImWchar ranges[] =
4761
0
    {
4762
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4763
0
        0,
4764
0
    };
4765
0
    return &ranges[0];
4766
0
}
4767
4768
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
4769
const ImWchar*   ImFontAtlas::GetGlyphRangesGreek()
4770
0
{
4771
0
    static const ImWchar ranges[] =
4772
0
    {
4773
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4774
0
        0x0370, 0x03FF, // Greek and Coptic
4775
0
        0,
4776
0
    };
4777
0
    return &ranges[0];
4778
0
}
4779
4780
const ImWchar*  ImFontAtlas::GetGlyphRangesKorean()
4781
0
{
4782
0
    static const ImWchar ranges[] =
4783
0
    {
4784
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4785
0
        0x3131, 0x3163, // Korean alphabets
4786
0
        0xAC00, 0xD7A3, // Korean characters
4787
0
        0xFFFD, 0xFFFD, // Invalid
4788
0
        0,
4789
0
    };
4790
0
    return &ranges[0];
4791
0
}
4792
4793
const ImWchar*  ImFontAtlas::GetGlyphRangesChineseFull()
4794
0
{
4795
0
    static const ImWchar ranges[] =
4796
0
    {
4797
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4798
0
        0x2000, 0x206F, // General Punctuation
4799
0
        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
4800
0
        0x31F0, 0x31FF, // Katakana Phonetic Extensions
4801
0
        0xFF00, 0xFFEF, // Half-width characters
4802
0
        0xFFFD, 0xFFFD, // Invalid
4803
0
        0x4e00, 0x9FAF, // CJK Ideograms
4804
0
        0,
4805
0
    };
4806
0
    return &ranges[0];
4807
0
}
4808
4809
static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges)
4810
0
{
4811
0
    for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2)
4812
0
    {
4813
0
        out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]);
4814
0
        base_codepoint += accumulative_offsets[n];
4815
0
    }
4816
0
    out_ranges[0] = 0;
4817
0
}
4818
4819
const ImWchar*  ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
4820
0
{
4821
    // Store 2500 regularly used characters for Simplified Chinese.
4822
    // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8
4823
    // This table covers 97.97% of all characters used during the month in July, 1987.
4824
    // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
4825
    // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)
4826
0
    static const short accumulative_offsets_from_0x4E00[] =
4827
0
    {
4828
0
        0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2,
4829
0
        1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4,
4830
0
        2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1,
4831
0
        1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2,
4832
0
        3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6,
4833
0
        1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1,
4834
0
        1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3,
4835
0
        2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4,
4836
0
        27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12,
4837
0
        3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1,
4838
0
        1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23,
4839
0
        176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6,
4840
0
        5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6,
4841
0
        1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1,
4842
0
        6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5,
4843
0
        2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15,
4844
0
        2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6,
4845
0
        2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4,
4846
0
        3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5,
4847
0
        3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2,
4848
0
        3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16,
4849
0
        1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31,
4850
0
        140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7,
4851
0
        5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2,
4852
0
        2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13,
4853
0
        4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3,
4854
0
        2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4,
4855
0
        4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1,
4856
0
        3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3,
4857
0
        3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11,
4858
0
        2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9,
4859
0
        5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2,
4860
0
        3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3,
4861
0
        1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12,
4862
0
        4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8,
4863
0
        4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5,
4864
0
        26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1,
4865
0
        3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5,
4866
0
        2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6,
4867
0
        10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6
4868
0
    };
4869
0
    static ImWchar base_ranges[] = // not zero-terminated
4870
0
    {
4871
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4872
0
        0x2000, 0x206F, // General Punctuation
4873
0
        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
4874
0
        0x31F0, 0x31FF, // Katakana Phonetic Extensions
4875
0
        0xFF00, 0xFFEF, // Half-width characters
4876
0
        0xFFFD, 0xFFFD  // Invalid
4877
0
    };
4878
0
    static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 };
4879
0
    if (!full_ranges[0])
4880
0
    {
4881
0
        memcpy(full_ranges, base_ranges, sizeof(base_ranges));
4882
0
        UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges));
4883
0
    }
4884
0
    return &full_ranges[0];
4885
0
}
4886
4887
const ImWchar*  ImFontAtlas::GetGlyphRangesJapanese()
4888
0
{
4889
    // 2999 ideograms code points for Japanese
4890
    // - 2136 Joyo (meaning "for regular use" or "for common use") Kanji code points
4891
    // - 863 Jinmeiyo (meaning "for personal name") Kanji code points
4892
    // - Sourced from official information provided by the government agencies of Japan:
4893
    //   - List of Joyo Kanji by the Agency for Cultural Affairs
4894
    //     - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/
4895
    //   - List of Jinmeiyo Kanji by the Ministry of Justice
4896
    //     - http://www.moj.go.jp/MINJI/minji86.html
4897
    //   - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0).
4898
    //     - https://creativecommons.org/licenses/by/4.0/legalcode
4899
    // - You can generate this code by the script at:
4900
    //   - https://github.com/vaiorabbit/everyday_use_kanji
4901
    // - References:
4902
    //   - List of Joyo Kanji
4903
    //     - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji
4904
    //   - List of Jinmeiyo Kanji
4905
    //     - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji
4906
    // - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.
4907
    // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
4908
    // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)
4909
0
    static const short accumulative_offsets_from_0x4E00[] =
4910
0
    {
4911
0
        0,1,2,4,1,1,1,1,2,1,3,3,2,2,1,5,3,5,7,5,6,1,2,1,7,2,6,3,1,8,1,1,4,1,1,18,2,11,2,6,2,1,2,1,5,1,2,1,3,1,2,1,2,3,3,1,1,2,3,1,1,1,12,7,9,1,4,5,1,
4912
0
        1,2,1,10,1,1,9,2,2,4,5,6,9,3,1,1,1,1,9,3,18,5,2,2,2,2,1,6,3,7,1,1,1,1,2,2,4,2,1,23,2,10,4,3,5,2,4,10,2,4,13,1,6,1,9,3,1,1,6,6,7,6,3,1,2,11,3,
4913
0
        2,2,3,2,15,2,2,5,4,3,6,4,1,2,5,2,12,16,6,13,9,13,2,1,1,7,16,4,7,1,19,1,5,1,2,2,7,7,8,2,6,5,4,9,18,7,4,5,9,13,11,8,15,2,1,1,1,2,1,2,2,1,2,2,8,
4914
0
        2,9,3,3,1,1,4,4,1,1,1,4,9,1,4,3,5,5,2,7,5,3,4,8,2,1,13,2,3,3,1,14,1,1,4,5,1,3,6,1,5,2,1,1,3,3,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1,1,1,1,12,3,3,9,5,
4915
0
        2,6,1,5,6,1,2,3,18,2,4,14,4,1,3,6,1,1,6,3,5,5,3,2,2,2,2,12,3,1,4,2,3,2,3,11,1,7,4,1,2,1,3,17,1,9,1,24,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,2,4,15,1,
4916
0
        1,2,1,1,2,1,5,2,5,20,2,5,9,1,10,8,7,6,1,1,1,1,1,1,6,2,1,2,8,1,1,1,1,5,1,1,3,1,1,1,1,3,1,1,12,4,1,3,1,1,1,1,1,10,3,1,7,5,13,1,2,3,4,6,1,1,30,
4917
0
        2,9,9,1,15,38,11,3,1,8,24,7,1,9,8,10,2,1,9,31,2,13,6,2,9,4,49,5,2,15,2,1,10,2,1,1,1,2,2,6,15,30,35,3,14,18,8,1,16,10,28,12,19,45,38,1,3,2,3,
4918
0
        13,2,1,7,3,6,5,3,4,3,1,5,7,8,1,5,3,18,5,3,6,1,21,4,24,9,24,40,3,14,3,21,3,2,1,2,4,2,3,1,15,15,6,5,1,1,3,1,5,6,1,9,7,3,3,2,1,4,3,8,21,5,16,4,
4919
0
        5,2,10,11,11,3,6,3,2,9,3,6,13,1,2,1,1,1,1,11,12,6,6,1,4,2,6,5,2,1,1,3,3,6,13,3,1,1,5,1,2,3,3,14,2,1,2,2,2,5,1,9,5,1,1,6,12,3,12,3,4,13,2,14,
4920
0
        2,8,1,17,5,1,16,4,2,2,21,8,9,6,23,20,12,25,19,9,38,8,3,21,40,25,33,13,4,3,1,4,1,2,4,1,2,5,26,2,1,1,2,1,3,6,2,1,1,1,1,1,1,2,3,1,1,1,9,2,3,1,1,
4921
0
        1,3,6,3,2,1,1,6,6,1,8,2,2,2,1,4,1,2,3,2,7,3,2,4,1,2,1,2,2,1,1,1,1,1,3,1,2,5,4,10,9,4,9,1,1,1,1,1,1,5,3,2,1,6,4,9,6,1,10,2,31,17,8,3,7,5,40,1,
4922
0
        7,7,1,6,5,2,10,7,8,4,15,39,25,6,28,47,18,10,7,1,3,1,1,2,1,1,1,3,3,3,1,1,1,3,4,2,1,4,1,3,6,10,7,8,6,2,2,1,3,3,2,5,8,7,9,12,2,15,1,1,4,1,2,1,1,
4923
0
        1,3,2,1,3,3,5,6,2,3,2,10,1,4,2,8,1,1,1,11,6,1,21,4,16,3,1,3,1,4,2,3,6,5,1,3,1,1,3,3,4,6,1,1,10,4,2,7,10,4,7,4,2,9,4,3,1,1,1,4,1,8,3,4,1,3,1,
4924
0
        6,1,4,2,1,4,7,2,1,8,1,4,5,1,1,2,2,4,6,2,7,1,10,1,1,3,4,11,10,8,21,4,6,1,3,5,2,1,2,28,5,5,2,3,13,1,2,3,1,4,2,1,5,20,3,8,11,1,3,3,3,1,8,10,9,2,
4925
0
        10,9,2,3,1,1,2,4,1,8,3,6,1,7,8,6,11,1,4,29,8,4,3,1,2,7,13,1,4,1,6,2,6,12,12,2,20,3,2,3,6,4,8,9,2,7,34,5,1,18,6,1,1,4,4,5,7,9,1,2,2,4,3,4,1,7,
4926
0
        2,2,2,6,2,3,25,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,5,3,4,4,3,2,1,1,4,1,2,1,1,3,1,11,1,6,3,1,7,3,6,2,8,8,6,9,3,4,11,3,2,10,12,2,5,11,1,6,4,5,
4927
0
        3,1,8,5,4,6,6,3,5,1,1,3,2,1,2,2,6,17,12,1,10,1,6,12,1,6,6,19,9,6,16,1,13,4,4,15,7,17,6,11,9,15,12,6,7,2,1,2,2,15,9,3,21,4,6,49,18,7,3,2,3,1,
4928
0
        6,8,2,2,6,2,9,1,3,6,4,4,1,2,16,2,5,2,1,6,2,3,5,3,1,2,5,1,2,1,9,3,1,8,6,4,8,11,3,1,1,1,1,3,1,13,8,4,1,3,2,2,1,4,1,11,1,5,2,1,5,2,5,8,6,1,1,7,
4929
0
        4,3,8,3,2,7,2,1,5,1,5,2,4,7,6,2,8,5,1,11,4,5,3,6,18,1,2,13,3,3,1,21,1,1,4,1,4,1,1,1,8,1,2,2,7,1,2,4,2,2,9,2,1,1,1,4,3,6,3,12,5,1,1,1,5,6,3,2,
4930
0
        4,8,2,2,4,2,7,1,8,9,5,2,3,2,1,3,2,13,7,14,6,5,1,1,2,1,4,2,23,2,1,1,6,3,1,4,1,15,3,1,7,3,9,14,1,3,1,4,1,1,5,8,1,3,8,3,8,15,11,4,14,4,4,2,5,5,
4931
0
        1,7,1,6,14,7,7,8,5,15,4,8,6,5,6,2,1,13,1,20,15,11,9,2,5,6,2,11,2,6,2,5,1,5,8,4,13,19,25,4,1,1,11,1,34,2,5,9,14,6,2,2,6,1,1,14,1,3,14,13,1,6,
4932
0
        12,21,14,14,6,32,17,8,32,9,28,1,2,4,11,8,3,1,14,2,5,15,1,1,1,1,3,6,4,1,3,4,11,3,1,1,11,30,1,5,1,4,1,5,8,1,1,3,2,4,3,17,35,2,6,12,17,3,1,6,2,
4933
0
        1,1,12,2,7,3,3,2,1,16,2,8,3,6,5,4,7,3,3,8,1,9,8,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,4,3,7,5,8,3,3,3,3,3,3,1,23,10,3,1,2,2,6,3,1,16,1,16,
4934
0
        22,3,10,4,11,6,9,7,7,3,6,2,2,2,4,10,2,1,1,2,8,7,1,6,4,1,3,3,3,5,10,12,12,2,3,12,8,15,1,1,16,6,6,1,5,9,11,4,11,4,2,6,12,1,17,5,13,1,4,9,5,1,11,
4935
0
        2,1,8,1,5,7,28,8,3,5,10,2,17,3,38,22,1,2,18,12,10,4,38,18,1,4,44,19,4,1,8,4,1,12,1,4,31,12,1,14,7,75,7,5,10,6,6,13,3,2,11,11,3,2,5,28,15,6,18,
4936
0
        18,5,6,4,3,16,1,7,18,7,36,3,5,3,1,7,1,9,1,10,7,2,4,2,6,2,9,7,4,3,32,12,3,7,10,2,23,16,3,1,12,3,31,4,11,1,3,8,9,5,1,30,15,6,12,3,2,2,11,19,9,
4937
0
        14,2,6,2,3,19,13,17,5,3,3,25,3,14,1,1,1,36,1,3,2,19,3,13,36,9,13,31,6,4,16,34,2,5,4,2,3,3,5,1,1,1,4,3,1,17,3,2,3,5,3,1,3,2,3,5,6,3,12,11,1,3,
4938
0
        1,2,26,7,12,7,2,14,3,3,7,7,11,25,25,28,16,4,36,1,2,1,6,2,1,9,3,27,17,4,3,4,13,4,1,3,2,2,1,10,4,2,4,6,3,8,2,1,18,1,1,24,2,2,4,33,2,3,63,7,1,6,
4939
0
        40,7,3,4,4,2,4,15,18,1,16,1,1,11,2,41,14,1,3,18,13,3,2,4,16,2,17,7,15,24,7,18,13,44,2,2,3,6,1,1,7,5,1,7,1,4,3,3,5,10,8,2,3,1,8,1,1,27,4,2,1,
4940
0
        12,1,2,1,10,6,1,6,7,5,2,3,7,11,5,11,3,6,6,2,3,15,4,9,1,1,2,1,2,11,2,8,12,8,5,4,2,3,1,5,2,2,1,14,1,12,11,4,1,11,17,17,4,3,2,5,5,7,3,1,5,9,9,8,
4941
0
        2,5,6,6,13,13,2,1,2,6,1,2,2,49,4,9,1,2,10,16,7,8,4,3,2,23,4,58,3,29,1,14,19,19,11,11,2,7,5,1,3,4,6,2,18,5,12,12,17,17,3,3,2,4,1,6,2,3,4,3,1,
4942
0
        1,1,1,5,1,1,9,1,3,1,3,6,1,8,1,1,2,6,4,14,3,1,4,11,4,1,3,32,1,2,4,13,4,1,2,4,2,1,3,1,11,1,4,2,1,4,4,6,3,5,1,6,5,7,6,3,23,3,5,3,5,3,3,13,3,9,10,
4943
0
        1,12,10,2,3,18,13,7,160,52,4,2,2,3,2,14,5,4,12,4,6,4,1,20,4,11,6,2,12,27,1,4,1,2,2,7,4,5,2,28,3,7,25,8,3,19,3,6,10,2,2,1,10,2,5,4,1,3,4,1,5,
4944
0
        3,2,6,9,3,6,2,16,3,3,16,4,5,5,3,2,1,2,16,15,8,2,6,21,2,4,1,22,5,8,1,1,21,11,2,1,11,11,19,13,12,4,2,3,2,3,6,1,8,11,1,4,2,9,5,2,1,11,2,9,1,1,2,
4945
0
        14,31,9,3,4,21,14,4,8,1,7,2,2,2,5,1,4,20,3,3,4,10,1,11,9,8,2,1,4,5,14,12,14,2,17,9,6,31,4,14,1,20,13,26,5,2,7,3,6,13,2,4,2,19,6,2,2,18,9,3,5,
4946
0
        12,12,14,4,6,2,3,6,9,5,22,4,5,25,6,4,8,5,2,6,27,2,35,2,16,3,7,8,8,6,6,5,9,17,2,20,6,19,2,13,3,1,1,1,4,17,12,2,14,7,1,4,18,12,38,33,2,10,1,1,
4947
0
        2,13,14,17,11,50,6,33,20,26,74,16,23,45,50,13,38,33,6,6,7,4,4,2,1,3,2,5,8,7,8,9,3,11,21,9,13,1,3,10,6,7,1,2,2,18,5,5,1,9,9,2,68,9,19,13,2,5,
4948
0
        1,4,4,7,4,13,3,9,10,21,17,3,26,2,1,5,2,4,5,4,1,7,4,7,3,4,2,1,6,1,1,20,4,1,9,2,2,1,3,3,2,3,2,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,3,2,10,3,5,3,4,4,
4949
0
        3,4,16,1,6,1,10,2,4,2,1,1,2,10,11,2,2,3,1,24,31,4,10,10,2,5,12,16,164,15,4,16,7,9,15,19,17,1,2,1,1,5,1,1,1,1,1,3,1,4,3,1,3,1,3,1,2,1,1,3,3,7,
4950
0
        2,8,1,2,2,2,1,3,4,3,7,8,12,92,2,10,3,1,3,14,5,25,16,42,4,7,7,4,2,21,5,27,26,27,21,25,30,31,2,1,5,13,3,22,5,6,6,11,9,12,1,5,9,7,5,5,22,60,3,5,
4951
0
        13,1,1,8,1,1,3,3,2,1,9,3,3,18,4,1,2,3,7,6,3,1,2,3,9,1,3,1,3,2,1,3,1,1,1,2,1,11,3,1,6,9,1,3,2,3,1,2,1,5,1,1,4,3,4,1,2,2,4,4,1,7,2,1,2,2,3,5,13,
4952
0
        18,3,4,14,9,9,4,16,3,7,5,8,2,6,48,28,3,1,1,4,2,14,8,2,9,2,1,15,2,4,3,2,10,16,12,8,7,1,1,3,1,1,1,2,7,4,1,6,4,38,39,16,23,7,15,15,3,2,12,7,21,
4953
0
        37,27,6,5,4,8,2,10,8,8,6,5,1,2,1,3,24,1,16,17,9,23,10,17,6,1,51,55,44,13,294,9,3,6,2,4,2,2,15,1,1,1,13,21,17,68,14,8,9,4,1,4,9,3,11,7,1,1,1,
4954
0
        5,6,3,2,1,1,1,2,3,8,1,2,2,4,1,5,5,2,1,4,3,7,13,4,1,4,1,3,1,1,1,5,5,10,1,6,1,5,2,1,5,2,4,1,4,5,7,3,18,2,9,11,32,4,3,3,2,4,7,11,16,9,11,8,13,38,
4955
0
        32,8,4,2,1,1,2,1,2,4,4,1,1,1,4,1,21,3,11,1,16,1,1,6,1,3,2,4,9,8,57,7,44,1,3,3,13,3,10,1,1,7,5,2,7,21,47,63,3,15,4,7,1,16,1,1,2,8,2,3,42,15,4,
4956
0
        1,29,7,22,10,3,78,16,12,20,18,4,67,11,5,1,3,15,6,21,31,32,27,18,13,71,35,5,142,4,10,1,2,50,19,33,16,35,37,16,19,27,7,1,133,19,1,4,8,7,20,1,4,
4957
0
        4,1,10,3,1,6,1,2,51,5,40,15,24,43,22928,11,1,13,154,70,3,1,1,7,4,10,1,2,1,1,2,1,2,1,2,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,
4958
0
        3,2,1,1,1,1,2,1,1,
4959
0
    };
4960
0
    static ImWchar base_ranges[] = // not zero-terminated
4961
0
    {
4962
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4963
0
        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
4964
0
        0x31F0, 0x31FF, // Katakana Phonetic Extensions
4965
0
        0xFF00, 0xFFEF, // Half-width characters
4966
0
        0xFFFD, 0xFFFD  // Invalid
4967
0
    };
4968
0
    static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 };
4969
0
    if (!full_ranges[0])
4970
0
    {
4971
0
        memcpy(full_ranges, base_ranges, sizeof(base_ranges));
4972
0
        UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges));
4973
0
    }
4974
0
    return &full_ranges[0];
4975
0
}
4976
4977
const ImWchar*  ImFontAtlas::GetGlyphRangesCyrillic()
4978
0
{
4979
0
    static const ImWchar ranges[] =
4980
0
    {
4981
0
        0x0020, 0x00FF, // Basic Latin + Latin Supplement
4982
0
        0x0400, 0x052F, // Cyrillic + Cyrillic Supplement
4983
0
        0x2DE0, 0x2DFF, // Cyrillic Extended-A
4984
0
        0xA640, 0xA69F, // Cyrillic Extended-B
4985
0
        0,
4986
0
    };
4987
0
    return &ranges[0];
4988
0
}
4989
4990
const ImWchar*  ImFontAtlas::GetGlyphRangesThai()
4991
0
{
4992
0
    static const ImWchar ranges[] =
4993
0
    {
4994
0
        0x0020, 0x00FF, // Basic Latin
4995
0
        0x2010, 0x205E, // Punctuations
4996
0
        0x0E00, 0x0E7F, // Thai
4997
0
        0,
4998
0
    };
4999
0
    return &ranges[0];
5000
0
}
5001
5002
const ImWchar*  ImFontAtlas::GetGlyphRangesVietnamese()
5003
0
{
5004
0
    static const ImWchar ranges[] =
5005
0
    {
5006
0
        0x0020, 0x00FF, // Basic Latin
5007
0
        0x0102, 0x0103,
5008
0
        0x0110, 0x0111,
5009
0
        0x0128, 0x0129,
5010
0
        0x0168, 0x0169,
5011
0
        0x01A0, 0x01A1,
5012
0
        0x01AF, 0x01B0,
5013
0
        0x1EA0, 0x1EF9,
5014
0
        0,
5015
0
    };
5016
0
    return &ranges[0];
5017
0
}
5018
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5019
5020
//-----------------------------------------------------------------------------
5021
// [SECTION] ImFontGlyphRangesBuilder
5022
//-----------------------------------------------------------------------------
5023
5024
void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
5025
0
{
5026
0
    while (text_end ? (text < text_end) : *text)
5027
0
    {
5028
0
        unsigned int c = 0;
5029
0
        int c_len = ImTextCharFromUtf8(&c, text, text_end);
5030
0
        text += c_len;
5031
0
        if (c_len == 0)
5032
0
            break;
5033
0
        AddChar((ImWchar)c);
5034
0
    }
5035
0
}
5036
5037
void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)
5038
0
{
5039
0
    for (; ranges[0]; ranges += 2)
5040
0
        for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560
5041
0
            AddChar((ImWchar)c);
5042
0
}
5043
5044
void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
5045
0
{
5046
0
    const int max_codepoint = IM_UNICODE_CODEPOINT_MAX;
5047
0
    for (int n = 0; n <= max_codepoint; n++)
5048
0
        if (GetBit(n))
5049
0
        {
5050
0
            out_ranges->push_back((ImWchar)n);
5051
0
            while (n < max_codepoint && GetBit(n + 1))
5052
0
                n++;
5053
0
            out_ranges->push_back((ImWchar)n);
5054
0
        }
5055
0
    out_ranges->push_back(0);
5056
0
}
5057
5058
//-----------------------------------------------------------------------------
5059
// [SECTION] ImFont
5060
//-----------------------------------------------------------------------------
5061
5062
ImFontBaked::ImFontBaked()
5063
2
{
5064
2
    memset(this, 0, sizeof(*this));
5065
2
    FallbackGlyphIndex = -1;
5066
2
}
5067
5068
void ImFontBaked::ClearOutputData()
5069
1
{
5070
1
    FallbackAdvanceX = 0.0f;
5071
1
    Glyphs.clear();
5072
1
    IndexAdvanceX.clear();
5073
1
    IndexLookup.clear();
5074
1
    FallbackGlyphIndex = -1;
5075
1
    Ascent = Descent = 0.0f;
5076
1
    MetricsTotalSurface = 0;
5077
1
}
5078
5079
ImFont::ImFont()
5080
1
{
5081
1
    memset(this, 0, sizeof(*this));
5082
1
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
5083
1
    Scale = 1.0f;
5084
1
#endif
5085
1
}
5086
5087
ImFont::~ImFont()
5088
1
{
5089
1
    ClearOutputData();
5090
1
}
5091
5092
void ImFont::ClearOutputData()
5093
4
{
5094
4
    if (ImFontAtlas* atlas = ContainerAtlas)
5095
3
        ImFontAtlasFontDiscardBakes(atlas, this, 0);
5096
4
    FallbackChar = EllipsisChar = 0;
5097
4
    memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap));
5098
4
    LastBaked = NULL;
5099
4
}
5100
5101
// API is designed this way to avoid exposing the 8K page size
5102
// e.g. use with IsGlyphRangeUnused(0, 255)
5103
bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last)
5104
0
{
5105
0
    unsigned int page_begin = (c_begin / 8192);
5106
0
    unsigned int page_last = (c_last / 8192);
5107
0
    for (unsigned int page_n = page_begin; page_n <= page_last; page_n++)
5108
0
        if ((page_n >> 3) < sizeof(Used8kPagesMap))
5109
0
            if (Used8kPagesMap[page_n >> 3] & (1 << (page_n & 7)))
5110
0
                return false;
5111
0
    return true;
5112
0
}
5113
5114
// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.
5115
// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis).
5116
// - 'src' is not necessarily == 'this->Sources' because multiple source fonts+configs can be used to build one target font.
5117
ImFontGlyph* ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph)
5118
22
{
5119
22
    int glyph_idx = baked->Glyphs.Size;
5120
22
    baked->Glyphs.push_back(*in_glyph);
5121
22
    ImFontGlyph* glyph = &baked->Glyphs[glyph_idx];
5122
22
    IM_ASSERT(baked->Glyphs.Size < 0xFFFE); // IndexLookup[] hold 16-bit values and -1/-2 are reserved.
5123
5124
    // Set UV from packed rectangle
5125
22
    if (glyph->PackId != ImFontAtlasRectId_Invalid)
5126
20
    {
5127
20
        ImTextureRect* r = ImFontAtlasPackGetRect(atlas, glyph->PackId);
5128
20
        IM_ASSERT(glyph->U0 == 0.0f && glyph->V0 == 0.0f && glyph->U1 == 0.0f && glyph->V1 == 0.0f);
5129
20
        glyph->U0 = (r->x) * atlas->TexUvScale.x;
5130
20
        glyph->V0 = (r->y) * atlas->TexUvScale.y;
5131
20
        glyph->U1 = (r->x + r->w) * atlas->TexUvScale.x;
5132
20
        glyph->V1 = (r->y + r->h) * atlas->TexUvScale.y;
5133
20
        baked->MetricsTotalSurface += r->w * r->h;
5134
20
    }
5135
5136
22
    if (src != NULL)
5137
22
    {
5138
        // Clamp & recenter if needed
5139
22
        const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
5140
22
        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
5141
22
        float advance_x = ImClamp(glyph->AdvanceX, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
5142
22
        if (advance_x != glyph->AdvanceX)
5143
0
        {
5144
0
            float char_off_x = src->PixelSnapH ? ImTrunc((advance_x - glyph->AdvanceX) * 0.5f) : (advance_x - glyph->AdvanceX) * 0.5f;
5145
0
            glyph->X0 += char_off_x;
5146
0
            glyph->X1 += char_off_x;
5147
0
        }
5148
5149
        // Snap to pixel
5150
22
        if (src->PixelSnapH)
5151
22
            advance_x = IM_ROUND(advance_x);
5152
5153
        // Bake spacing
5154
22
        glyph->AdvanceX = advance_x + src->GlyphExtraAdvanceX;
5155
22
    }
5156
22
    if (glyph->Colored)
5157
0
        atlas->TexPixelsUseColors = atlas->TexData->UseColors = true;
5158
5159
    // Update lookup tables
5160
22
    const int codepoint = glyph->Codepoint;
5161
22
    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
5162
22
    baked->IndexAdvanceX[codepoint] = glyph->AdvanceX;
5163
22
    baked->IndexLookup[codepoint] = (ImU16)glyph_idx;
5164
22
    const int page_n = codepoint / 8192;
5165
22
    baked->ContainerFont->Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7);
5166
5167
22
    return glyph;
5168
22
}
5169
5170
// FIXME: Code is duplicated with code above.
5171
void ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x)
5172
0
{
5173
0
    IM_UNUSED(atlas);
5174
0
    if (src != NULL)
5175
0
    {
5176
        // Clamp & recenter if needed
5177
0
        const float ref_size = baked->ContainerFont->Sources[0]->SizePixels;
5178
0
        const float offsets_scale = (ref_size != 0.0f) ? (baked->Size / ref_size) : 1.0f;
5179
0
        advance_x = ImClamp(advance_x, src->GlyphMinAdvanceX * offsets_scale, src->GlyphMaxAdvanceX * offsets_scale);
5180
5181
        // Snap to pixel
5182
0
        if (src->PixelSnapH)
5183
0
            advance_x = IM_ROUND(advance_x);
5184
5185
        // Bake spacing
5186
0
        advance_x += src->GlyphExtraAdvanceX;
5187
0
    }
5188
5189
0
    ImFontBaked_BuildGrowIndex(baked, codepoint + 1);
5190
0
    baked->IndexAdvanceX[codepoint] = advance_x;
5191
0
}
5192
5193
// Copy to texture, post-process and queue update for backend
5194
void ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch)
5195
20
{
5196
20
    ImTextureData* tex = atlas->TexData;
5197
20
    IM_ASSERT(r->x + r->w <= tex->Width && r->y + r->h <= tex->Height);
5198
20
    ImFontAtlasTextureBlockConvert(src_pixels, src_fmt, src_pitch, (unsigned char*)tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h);
5199
20
    ImFontAtlasPostProcessData pp_data = { atlas, baked->ContainerFont, src, baked, glyph, tex->GetPixelsAt(r->x, r->y), tex->Format, tex->GetPitch(), r->w, r->h };
5200
20
    ImFontAtlasTextureBlockPostProcess(&pp_data);
5201
20
    ImFontAtlasTextureBlockQueueUpload(atlas, tex, r->x, r->y, r->w, r->h);
5202
20
}
5203
5204
void ImFont::AddRemapChar(ImWchar from_codepoint, ImWchar to_codepoint)
5205
0
{
5206
0
    RemapPairs.SetInt((ImGuiID)from_codepoint, (int)to_codepoint);
5207
0
}
5208
5209
// Find glyph, load if necessary, return fallback if missing
5210
ImFontGlyph* ImFontBaked::FindGlyph(ImWchar c)
5211
1.54M
{
5212
1.54M
    if (c < (size_t)IndexLookup.Size) IM_LIKELY
5213
1.54M
    {
5214
1.54M
        const int i = (int)IndexLookup.Data[c];
5215
1.54M
        if (i == IM_FONTGLYPH_INDEX_NOT_FOUND)
5216
0
            return &Glyphs.Data[FallbackGlyphIndex];
5217
1.54M
        if (i != IM_FONTGLYPH_INDEX_UNUSED)
5218
1.54M
            return &Glyphs.Data[i];
5219
1.54M
    }
5220
0
    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);
5221
0
    return glyph ? glyph : &Glyphs.Data[FallbackGlyphIndex];
5222
1.54M
}
5223
5224
// Attempt to load but when missing, return NULL instead of FallbackGlyph
5225
ImFontGlyph* ImFontBaked::FindGlyphNoFallback(ImWchar c)
5226
2
{
5227
2
    if (c < (size_t)IndexLookup.Size) IM_LIKELY
5228
1
    {
5229
1
        const int i = (int)IndexLookup.Data[c];
5230
1
        if (i == IM_FONTGLYPH_INDEX_NOT_FOUND)
5231
0
            return NULL;
5232
1
        if (i != IM_FONTGLYPH_INDEX_UNUSED)
5233
0
            return &Glyphs.Data[i];
5234
1
    }
5235
2
    LoadNoFallback = true; // This is actually a rare call, not done in hot-loop, so we prioritize not adding extra cruft to ImFontBaked_BuildLoadGlyph() call sites.
5236
2
    ImFontGlyph* glyph = ImFontBaked_BuildLoadGlyph(this, c, NULL);
5237
2
    LoadNoFallback = false;
5238
2
    return glyph;
5239
2
}
5240
5241
bool ImFontBaked::IsGlyphLoaded(ImWchar c)
5242
0
{
5243
0
    if (c < (size_t)IndexLookup.Size) IM_LIKELY
5244
0
    {
5245
0
        const int i = (int)IndexLookup.Data[c];
5246
0
        if (i == IM_FONTGLYPH_INDEX_NOT_FOUND)
5247
0
            return false;
5248
0
        if (i != IM_FONTGLYPH_INDEX_UNUSED)
5249
0
            return true;
5250
0
    }
5251
0
    return false;
5252
0
}
5253
5254
// This is not fast query
5255
bool ImFont::IsGlyphInFont(ImWchar c)
5256
3
{
5257
3
    ImFontAtlas* atlas = ContainerAtlas;
5258
3
    ImFontAtlas_FontHookRemapCodepoint(atlas, this, &c);
5259
3
    for (ImFontConfig* src : Sources)
5260
3
    {
5261
3
        const ImFontLoader* loader = src->FontLoader ? src->FontLoader : atlas->FontLoader;
5262
3
        if (loader->FontSrcContainsGlyph != NULL && loader->FontSrcContainsGlyph(atlas, src, c))
5263
2
            return true;
5264
3
    }
5265
1
    return false;
5266
3
}
5267
5268
// This is manually inlined in CalcTextSizeA() and CalcWordWrapPosition(), with a non-inline call to BuildLoadGlyphGetAdvanceOrFallback().
5269
IM_MSVC_RUNTIME_CHECKS_OFF
5270
float ImFontBaked::GetCharAdvance(ImWchar c)
5271
0
{
5272
0
    if ((int)c < IndexAdvanceX.Size)
5273
0
    {
5274
        // Missing glyphs fitting inside index will have stored FallbackAdvanceX already.
5275
0
        const float x = IndexAdvanceX.Data[c];
5276
0
        if (x >= 0.0f)
5277
0
            return x;
5278
0
    }
5279
0
    return ImFontBaked_BuildLoadGlyphAdvanceX(this, c);
5280
0
}
5281
IM_MSVC_RUNTIME_CHECKS_RESTORE
5282
5283
ImGuiID ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density)
5284
80.5k
{
5285
80.5k
    struct { ImGuiID FontId; float BakedSize; float RasterizerDensity; } hashed_data;
5286
80.5k
    hashed_data.FontId = font_id;
5287
80.5k
    hashed_data.BakedSize = baked_size;
5288
80.5k
    hashed_data.RasterizerDensity = rasterizer_density;
5289
80.5k
    return ImHashData(&hashed_data, sizeof(hashed_data));
5290
80.5k
}
5291
5292
// ImFontBaked pointers are valid for the entire frame but shall never be kept between frames.
5293
ImFontBaked* ImFont::GetFontBaked(float size, float density)
5294
891k
{
5295
891k
    ImFontBaked* baked = LastBaked;
5296
5297
    // Round font size
5298
    // - ImGui::PushFont() will already round, but other paths calling GetFontBaked() directly also needs it (e.g. ImFontAtlasBuildPreloadAllGlyphRanges)
5299
891k
    size = ImGui::GetRoundedFontSize(size);
5300
5301
891k
    if (density < 0.0f)
5302
891k
        density = CurrentRasterizerDensity;
5303
891k
    if (baked && baked->Size == size && baked->RasterizerDensity == density)
5304
811k
        return baked;
5305
5306
80.5k
    ImFontAtlas* atlas = ContainerAtlas;
5307
80.5k
    ImFontAtlasBuilder* builder = atlas->Builder;
5308
80.5k
    baked = ImFontAtlasBakedGetOrAdd(atlas, this, size, density);
5309
80.5k
    if (baked == NULL)
5310
0
        return NULL;
5311
80.5k
    baked->LastUsedFrame = builder->FrameCount;
5312
80.5k
    LastBaked = baked;
5313
80.5k
    return baked;
5314
80.5k
}
5315
5316
ImFontBaked* ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density)
5317
80.5k
{
5318
    // FIXME-NEWATLAS: Design for picking a nearest size based on some criteria?
5319
    // FIXME-NEWATLAS: Altering font density won't work right away.
5320
80.5k
    IM_ASSERT(font_size > 0.0f && font_rasterizer_density > 0.0f);
5321
80.5k
    ImGuiID baked_id = ImFontAtlasBakedGetId(font->FontId, font_size, font_rasterizer_density);
5322
80.5k
    ImFontAtlasBuilder* builder = atlas->Builder;
5323
80.5k
    ImFontBaked** p_baked_in_map = (ImFontBaked**)builder->BakedMap.GetVoidPtrRef(baked_id);
5324
80.5k
    ImFontBaked* baked = *p_baked_in_map;
5325
80.5k
    if (baked != NULL)
5326
80.5k
    {
5327
80.5k
        IM_ASSERT(baked->Size == font_size && baked->ContainerFont == font && baked->BakedId == baked_id);
5328
80.5k
        return baked;
5329
80.5k
    }
5330
5331
    // If atlas is locked, find closest match
5332
    // FIXME-OPT: This is not an optimal query.
5333
1
    if ((font->Flags & ImFontFlags_LockBakedSizes) || atlas->Locked)
5334
0
    {
5335
0
        baked = ImFontAtlasBakedGetClosestMatch(atlas, font, font_size, font_rasterizer_density);
5336
0
        if (baked != NULL)
5337
0
            return baked;
5338
0
        if (atlas->Locked)
5339
0
        {
5340
0
            IM_ASSERT(!atlas->Locked && "Cannot use dynamic font size with a locked ImFontAtlas!"); // Locked because rendering backend does not support ImGuiBackendFlags_RendererHasTextures!
5341
0
            return NULL;
5342
0
        }
5343
0
    }
5344
5345
    // Create new
5346
1
    baked = ImFontAtlasBakedAdd(atlas, font, font_size, font_rasterizer_density, baked_id);
5347
1
    *p_baked_in_map = baked; // To avoid 'builder->BakedMap.SetVoidPtr(baked_id, baked);' while we can.
5348
1
    return baked;
5349
1
}
5350
5351
// Trim trailing space and find beginning of next line
5352
const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags)
5353
0
{
5354
0
    if ((flags & ImDrawTextFlags_WrapKeepBlanks) == 0)
5355
0
        while (text < text_end && ImCharIsBlankA(*text))
5356
0
            text++;
5357
0
    if (*text == '\n')
5358
0
        text++;
5359
0
    return text;
5360
0
}
5361
5362
// Simple word-wrapping for English, not full-featured. Please submit failing cases!
5363
// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
5364
// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
5365
const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags)
5366
0
{
5367
    // For references, possible wrap point marked with ^
5368
    //  "aaa bbb, ccc,ddd. eee   fff. ggg!"
5369
    //      ^    ^    ^   ^   ^__    ^    ^
5370
5371
    // List of hardcoded separators: .,;!?'"
5372
5373
    // Skip extra blanks after a line returns (that includes not counting them in width computation)
5374
    // e.g. "Hello    world" --> "Hello" "World"
5375
5376
    // Cut words that cannot possibly fit within one line.
5377
    // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish"
5378
5379
0
    ImFontBaked* baked = font->GetFontBaked(size);
5380
0
    const float scale = size / baked->Size;
5381
5382
0
    float line_width = 0.0f;
5383
0
    float word_width = 0.0f;
5384
0
    float blank_width = 0.0f;
5385
0
    wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
5386
5387
0
    const char* word_end = text;
5388
0
    const char* prev_word_end = NULL;
5389
0
    bool inside_word = true;
5390
5391
0
    const char* s = text;
5392
0
    IM_ASSERT(text_end != NULL);
5393
0
    while (s < text_end)
5394
0
    {
5395
0
        unsigned int c = (unsigned int)*s;
5396
0
        const char* next_s;
5397
0
        if (c < 0x80)
5398
0
            next_s = s + 1;
5399
0
        else
5400
0
            next_s = s + ImTextCharFromUtf8(&c, s, text_end);
5401
5402
0
        if (c < 32)
5403
0
        {
5404
0
            if (c == '\n')
5405
0
                return s; // Direct return, skip "Wrap_width is too small to fit anything" path.
5406
0
            if (c == '\r')
5407
0
            {
5408
0
                s = next_s;
5409
0
                continue;
5410
0
            }
5411
0
        }
5412
5413
        // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
5414
0
        float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
5415
0
        if (char_width < 0.0f)
5416
0
            char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c);
5417
5418
0
        if (ImCharIsBlankW(c))
5419
0
        {
5420
0
            if (inside_word)
5421
0
            {
5422
0
                line_width += blank_width;
5423
0
                blank_width = 0.0f;
5424
0
                word_end = s;
5425
0
            }
5426
0
            blank_width += char_width;
5427
0
            inside_word = false;
5428
0
        }
5429
0
        else
5430
0
        {
5431
0
            word_width += char_width;
5432
0
            if (inside_word)
5433
0
            {
5434
0
                word_end = next_s;
5435
0
            }
5436
0
            else
5437
0
            {
5438
0
                prev_word_end = word_end;
5439
0
                line_width += word_width + blank_width;
5440
0
                if ((flags & ImDrawTextFlags_WrapKeepBlanks) && line_width <= wrap_width)
5441
0
                    prev_word_end = s;
5442
0
                word_width = blank_width = 0.0f;
5443
0
            }
5444
5445
            // Allow wrapping after punctuation.
5446
0
            inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\"' && c != 0x3001 && c != 0x3002);
5447
0
        }
5448
5449
        // We ignore blank width at the end of the line (they can be skipped)
5450
0
        if (line_width + word_width > wrap_width)
5451
0
        {
5452
            // Words that cannot possibly fit within an entire line will be cut anywhere.
5453
0
            if (word_width < wrap_width)
5454
0
                s = prev_word_end ? prev_word_end : word_end;
5455
0
            break;
5456
0
        }
5457
5458
0
        s = next_s;
5459
0
    }
5460
5461
    // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
5462
    // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).
5463
0
    if (s == text && text < text_end)
5464
0
        return s + ImTextCountUtf8BytesFromChar(s, text_end);
5465
0
    return s;
5466
0
}
5467
5468
const char* ImFont::CalcWordWrapPosition(float size, const char* text, const char* text_end, float wrap_width)
5469
0
{
5470
0
    return ImFontCalcWordWrapPositionEx(this, size, text, text_end, wrap_width, ImDrawTextFlags_None);
5471
0
}
5472
5473
ImVec2 ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
5474
324k
{
5475
324k
    if (!text_end)
5476
0
        text_end = text_begin + ImStrlen(text_begin); // FIXME-OPT: Need to avoid this.
5477
324k
    if (!text_end_display)
5478
0
        text_end_display = text_end;
5479
5480
324k
    ImFontBaked* baked = font->GetFontBaked(size);
5481
324k
    const float line_height = size;
5482
324k
    const float scale = line_height / baked->Size;
5483
5484
324k
    ImVec2 text_size = ImVec2(0, 0);
5485
324k
    float line_width = 0.0f;
5486
5487
324k
    const bool word_wrap_enabled = (wrap_width > 0.0f);
5488
324k
    const char* word_wrap_eol = NULL;
5489
5490
324k
    const char* s = text_begin;
5491
1.86M
    while (s < text_end_display)
5492
1.54M
    {
5493
        // Word-wrapping
5494
1.54M
        if (word_wrap_enabled)
5495
0
        {
5496
            // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
5497
0
            if (!word_wrap_eol)
5498
0
                word_wrap_eol = ImFontCalcWordWrapPositionEx(font, size, s, text_end, wrap_width - line_width, flags);
5499
5500
0
            if (s >= word_wrap_eol)
5501
0
            {
5502
0
                if (text_size.x < line_width)
5503
0
                    text_size.x = line_width;
5504
0
                text_size.y += line_height;
5505
0
                line_width = 0.0f;
5506
0
                s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
5507
0
                if (flags & ImDrawTextFlags_StopOnNewLine)
5508
0
                    break;
5509
0
                word_wrap_eol = NULL;
5510
0
                continue;
5511
0
            }
5512
0
        }
5513
5514
        // Decode and advance source
5515
1.54M
        const char* prev_s = s;
5516
1.54M
        unsigned int c = (unsigned int)*s;
5517
1.54M
        if (c < 0x80)
5518
1.54M
            s += 1;
5519
0
        else
5520
0
            s += ImTextCharFromUtf8(&c, s, text_end);
5521
5522
1.54M
        if (c == '\n')
5523
0
        {
5524
0
            text_size.x = ImMax(text_size.x, line_width);
5525
0
            text_size.y += line_height;
5526
0
            line_width = 0.0f;
5527
0
            if (flags & ImDrawTextFlags_StopOnNewLine)
5528
0
                break;
5529
0
            continue;
5530
0
        }
5531
1.54M
        if (c == '\r')
5532
0
            continue;
5533
5534
        // Optimized inline version of 'float char_width = GetCharAdvance((ImWchar)c);'
5535
1.54M
        float char_width = (c < (unsigned int)baked->IndexAdvanceX.Size) ? baked->IndexAdvanceX.Data[c] : -1.0f;
5536
1.54M
        if (char_width < 0.0f)
5537
20
            char_width = BuildLoadGlyphGetAdvanceOrFallback(baked, c);
5538
1.54M
        char_width *= scale;
5539
5540
1.54M
        if (line_width + char_width >= max_width)
5541
0
        {
5542
0
            s = prev_s;
5543
0
            break;
5544
0
        }
5545
5546
1.54M
        line_width += char_width;
5547
1.54M
    }
5548
5549
324k
    if (text_size.x < line_width)
5550
324k
        text_size.x = line_width;
5551
5552
324k
    if (out_offset != NULL)
5553
0
        *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \n
5554
5555
324k
    if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \n
5556
324k
        text_size.y += line_height;
5557
5558
324k
    if (out_remaining != NULL)
5559
0
        *out_remaining = s;
5560
5561
324k
    return text_size;
5562
324k
}
5563
5564
ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** out_remaining)
5565
324k
{
5566
324k
    return ImFontCalcTextSizeEx(this, size, max_width, wrap_width, text_begin, text_end, text_end, out_remaining, NULL, ImDrawTextFlags_None);
5567
324k
}
5568
5569
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
5570
void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c, const ImVec4* cpu_fine_clip)
5571
0
{
5572
0
    ImFontBaked* baked = GetFontBaked(size);
5573
0
    const ImFontGlyph* glyph = baked->FindGlyph(c);
5574
0
    if (!glyph || !glyph->Visible)
5575
0
        return;
5576
0
    if (glyph->Colored)
5577
0
        col |= ~IM_COL32_A_MASK;
5578
0
    float scale = (size >= 0.0f) ? (size / baked->Size) : 1.0f;
5579
0
    float x = IM_TRUNC(pos.x);
5580
0
    float y = IM_TRUNC(pos.y);
5581
5582
0
    float x1 = x + glyph->X0 * scale;
5583
0
    float x2 = x + glyph->X1 * scale;
5584
0
    if (cpu_fine_clip && (x1 > cpu_fine_clip->z || x2 < cpu_fine_clip->x))
5585
0
        return;
5586
0
    float y1 = y + glyph->Y0 * scale;
5587
0
    float y2 = y + glyph->Y1 * scale;
5588
0
    float u1 = glyph->U0;
5589
0
    float v1 = glyph->V0;
5590
0
    float u2 = glyph->U1;
5591
0
    float v2 = glyph->V1;
5592
5593
    // Always CPU fine clip. Code extracted from RenderText().
5594
    // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
5595
0
    if (cpu_fine_clip != NULL)
5596
0
    {
5597
0
        if (x1 < cpu_fine_clip->x) { u1 = u1 + (1.0f - (x2 - cpu_fine_clip->x) / (x2 - x1)) * (u2 - u1); x1 = cpu_fine_clip->x; }
5598
0
        if (y1 < cpu_fine_clip->y) { v1 = v1 + (1.0f - (y2 - cpu_fine_clip->y) / (y2 - y1)) * (v2 - v1); y1 = cpu_fine_clip->y; }
5599
0
        if (x2 > cpu_fine_clip->z) { u2 = u1 + ((cpu_fine_clip->z - x1) / (x2 - x1)) * (u2 - u1); x2 = cpu_fine_clip->z; }
5600
0
        if (y2 > cpu_fine_clip->w) { v2 = v1 + ((cpu_fine_clip->w - y1) / (y2 - y1)) * (v2 - v1); y2 = cpu_fine_clip->w; }
5601
0
        if (y1 >= y2)
5602
0
            return;
5603
0
    }
5604
0
    draw_list->PrimReserve(6, 4);
5605
0
    draw_list->PrimRectUV(ImVec2(x1, y1), ImVec2(x2, y2), ImVec2(u1, v1), ImVec2(u2, v2), col);
5606
0
}
5607
5608
// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.
5609
// DO NOT CALL DIRECTLY THIS WILL CHANGE WILDLY IN 2025-2025. Use ImDrawList::AddText().
5610
void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, ImDrawTextFlags flags)
5611
324k
{
5612
    // Align to be pixel perfect
5613
324k
begin:
5614
324k
    float x = IM_TRUNC(pos.x);
5615
324k
    float y = IM_TRUNC(pos.y);
5616
324k
    if (y > clip_rect.w)
5617
0
        return;
5618
5619
324k
    if (!text_end)
5620
0
        text_end = text_begin + ImStrlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.
5621
5622
324k
    const float line_height = size;
5623
324k
    ImFontBaked* baked = GetFontBaked(size);
5624
5625
324k
    const float scale = size / baked->Size;
5626
324k
    const float origin_x = x;
5627
324k
    const bool word_wrap_enabled = (wrap_width > 0.0f);
5628
5629
    // Fast-forward to first visible line
5630
324k
    const char* s = text_begin;
5631
324k
    if (y + line_height < clip_rect.y)
5632
0
        while (y + line_height < clip_rect.y && s < text_end)
5633
0
        {
5634
0
            const char* line_end = (const char*)ImMemchr(s, '\n', text_end - s);
5635
0
            if (word_wrap_enabled)
5636
0
            {
5637
                // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPosition().
5638
                // If the specs for CalcWordWrapPosition() were reworked to optionally return on \n we could combine both.
5639
                // However it is still better than nothing performing the fast-forward!
5640
0
                s = ImFontCalcWordWrapPositionEx(this, size, s, line_end ? line_end : text_end, wrap_width, flags);
5641
0
                s = ImTextCalcWordWrapNextLineStart(s, text_end, flags);
5642
0
            }
5643
0
            else
5644
0
            {
5645
0
                s = line_end ? line_end + 1 : text_end;
5646
0
            }
5647
0
            y += line_height;
5648
0
        }
5649
5650
    // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()
5651
    // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm)
5652
324k
    if (text_end - s > 10000 && !word_wrap_enabled)
5653
0
    {
5654
0
        const char* s_end = s;
5655
0
        float y_end = y;
5656
0
        while (y_end < clip_rect.w && s_end < text_end)
5657
0
        {
5658
0
            s_end = (const char*)ImMemchr(s_end, '\n', text_end - s_end);
5659
0
            s_end = s_end ? s_end + 1 : text_end;
5660
0
            y_end += line_height;
5661
0
        }
5662
0
        text_end = s_end;
5663
0
    }
5664
324k
    if (s == text_end)
5665
0
        return;
5666
5667
    // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)
5668
324k
    const int vtx_count_max = (int)(text_end - s) * 4;
5669
324k
    const int idx_count_max = (int)(text_end - s) * 6;
5670
324k
    const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
5671
324k
    draw_list->PrimReserve(idx_count_max, vtx_count_max);
5672
324k
    ImDrawVert*  vtx_write = draw_list->_VtxWritePtr;
5673
324k
    ImDrawIdx*   idx_write = draw_list->_IdxWritePtr;
5674
324k
    unsigned int vtx_index = draw_list->_VtxCurrentIdx;
5675
324k
    const int cmd_count = draw_list->CmdBuffer.Size;
5676
324k
    const bool cpu_fine_clip = (flags & ImDrawTextFlags_CpuFineClip) != 0;
5677
5678
324k
    const ImU32 col_untinted = col | ~IM_COL32_A_MASK;
5679
324k
    const char* word_wrap_eol = NULL;
5680
5681
1.86M
    while (s < text_end)
5682
1.54M
    {
5683
1.54M
        if (word_wrap_enabled)
5684
0
        {
5685
            // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.
5686
0
            if (!word_wrap_eol)
5687
0
                word_wrap_eol = ImFontCalcWordWrapPositionEx(this, size, s, text_end, wrap_width - (x - origin_x), flags);
5688
5689
0
            if (s >= word_wrap_eol)
5690
0
            {
5691
0
                x = origin_x;
5692
0
                y += line_height;
5693
0
                if (y > clip_rect.w)
5694
0
                    break; // break out of main loop
5695
0
                word_wrap_eol = NULL;
5696
0
                s = ImTextCalcWordWrapNextLineStart(s, text_end, flags); // Wrapping skips upcoming blanks
5697
0
                continue;
5698
0
            }
5699
0
        }
5700
5701
        // Decode and advance source
5702
1.54M
        unsigned int c = (unsigned int)*s;
5703
1.54M
        if (c < 0x80)
5704
1.54M
            s += 1;
5705
0
        else
5706
0
            s += ImTextCharFromUtf8(&c, s, text_end);
5707
5708
1.54M
        if (c < 32)
5709
0
        {
5710
0
            if (c == '\n')
5711
0
            {
5712
0
                x = origin_x;
5713
0
                y += line_height;
5714
0
                if (y > clip_rect.w)
5715
0
                    break; // break out of main loop
5716
0
                continue;
5717
0
            }
5718
0
            if (c == '\r')
5719
0
                continue;
5720
0
        }
5721
5722
1.54M
        const ImFontGlyph* glyph = baked->FindGlyph((ImWchar)c);
5723
        //if (glyph == NULL)
5724
        //    continue;
5725
5726
1.54M
        float char_width = glyph->AdvanceX * scale;
5727
1.54M
        if (glyph->Visible)
5728
1.54M
        {
5729
            // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w
5730
1.54M
            float x1 = x + glyph->X0 * scale;
5731
1.54M
            float x2 = x + glyph->X1 * scale;
5732
1.54M
            float y1 = y + glyph->Y0 * scale;
5733
1.54M
            float y2 = y + glyph->Y1 * scale;
5734
1.54M
            if (x1 <= clip_rect.z && x2 >= clip_rect.x)
5735
1.54M
            {
5736
                // Render a character
5737
1.54M
                float u1 = glyph->U0;
5738
1.54M
                float v1 = glyph->V0;
5739
1.54M
                float u2 = glyph->U1;
5740
1.54M
                float v2 = glyph->V1;
5741
5742
                // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.
5743
1.54M
                if (cpu_fine_clip)
5744
0
                {
5745
0
                    if (x1 < clip_rect.x)
5746
0
                    {
5747
0
                        u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);
5748
0
                        x1 = clip_rect.x;
5749
0
                    }
5750
0
                    if (y1 < clip_rect.y)
5751
0
                    {
5752
0
                        v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);
5753
0
                        y1 = clip_rect.y;
5754
0
                    }
5755
0
                    if (x2 > clip_rect.z)
5756
0
                    {
5757
0
                        u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
5758
0
                        x2 = clip_rect.z;
5759
0
                    }
5760
0
                    if (y2 > clip_rect.w)
5761
0
                    {
5762
0
                        v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
5763
0
                        y2 = clip_rect.w;
5764
0
                    }
5765
0
                    if (y1 >= y2)
5766
0
                    {
5767
0
                        x += char_width;
5768
0
                        continue;
5769
0
                    }
5770
0
                }
5771
5772
                // Support for untinted glyphs
5773
1.54M
                ImU32 glyph_col = glyph->Colored ? col_untinted : col;
5774
5775
                // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:
5776
1.54M
                {
5777
1.54M
                    vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;
5778
1.54M
                    vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;
5779
1.54M
                    vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;
5780
1.54M
                    vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;
5781
1.54M
                    idx_write[0] = (ImDrawIdx)(vtx_index); idx_write[1] = (ImDrawIdx)(vtx_index + 1); idx_write[2] = (ImDrawIdx)(vtx_index + 2);
5782
1.54M
                    idx_write[3] = (ImDrawIdx)(vtx_index); idx_write[4] = (ImDrawIdx)(vtx_index + 2); idx_write[5] = (ImDrawIdx)(vtx_index + 3);
5783
1.54M
                    vtx_write += 4;
5784
1.54M
                    vtx_index += 4;
5785
1.54M
                    idx_write += 6;
5786
1.54M
                }
5787
1.54M
            }
5788
1.54M
        }
5789
1.54M
        x += char_width;
5790
1.54M
    }
5791
5792
    // Edge case: calling RenderText() with unloaded glyphs triggering texture change. It doesn't happen via ImGui:: calls because CalcTextSize() is always used.
5793
324k
    if (cmd_count != draw_list->CmdBuffer.Size) //-V547
5794
0
    {
5795
0
        IM_ASSERT(draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount == 0);
5796
0
        draw_list->CmdBuffer.pop_back();
5797
0
        draw_list->PrimUnreserve(idx_count_max, vtx_count_max);
5798
0
        draw_list->AddDrawCmd();
5799
        //IMGUI_DEBUG_LOG("RenderText: cancel and retry to missing glyphs.\n"); // [DEBUG]
5800
        //draw_list->AddRectFilled(pos, pos + ImVec2(10, 10), IM_COL32(255, 0, 0, 255)); // [DEBUG]
5801
0
        goto begin;
5802
        //RenderText(draw_list, size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip); // FIXME-OPT: Would a 'goto begin' be better for code-gen?
5803
        //return;
5804
0
    }
5805
5806
    // Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action.
5807
324k
    draw_list->VtxBuffer.Size = (int)(vtx_write - draw_list->VtxBuffer.Data); // Same as calling shrink()
5808
324k
    draw_list->IdxBuffer.Size = (int)(idx_write - draw_list->IdxBuffer.Data);
5809
324k
    draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);
5810
324k
    draw_list->_VtxWritePtr = vtx_write;
5811
324k
    draw_list->_IdxWritePtr = idx_write;
5812
324k
    draw_list->_VtxCurrentIdx = vtx_index;
5813
324k
}
5814
5815
//-----------------------------------------------------------------------------
5816
// [SECTION] ImGui Internal Render Helpers
5817
//-----------------------------------------------------------------------------
5818
// Vaguely redesigned to stop accessing ImGui global state:
5819
// - RenderArrow()
5820
// - RenderBullet()
5821
// - RenderCheckMark()
5822
// - RenderArrowPointingAt()
5823
// - RenderRectFilledRangeH()
5824
// - RenderRectFilledWithHole()
5825
//-----------------------------------------------------------------------------
5826
// Function in need of a redesign (legacy mess)
5827
// - RenderColorRectWithAlphaCheckerboard()
5828
//-----------------------------------------------------------------------------
5829
5830
// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state
5831
void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)
5832
80.5k
{
5833
80.5k
    const float h = draw_list->_Data->FontSize * 1.00f;
5834
80.5k
    float r = h * 0.40f * scale;
5835
80.5k
    ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);
5836
5837
80.5k
    ImVec2 a, b, c;
5838
80.5k
    switch (dir)
5839
80.5k
    {
5840
0
    case ImGuiDir_Up:
5841
80.5k
    case ImGuiDir_Down:
5842
80.5k
        if (dir == ImGuiDir_Up) r = -r;
5843
80.5k
        a = ImVec2(+0.000f, +0.750f) * r;
5844
80.5k
        b = ImVec2(-0.866f, -0.750f) * r;
5845
80.5k
        c = ImVec2(+0.866f, -0.750f) * r;
5846
80.5k
        break;
5847
0
    case ImGuiDir_Left:
5848
0
    case ImGuiDir_Right:
5849
0
        if (dir == ImGuiDir_Left) r = -r;
5850
0
        a = ImVec2(+0.750f, +0.000f) * r;
5851
0
        b = ImVec2(-0.750f, +0.866f) * r;
5852
0
        c = ImVec2(-0.750f, -0.866f) * r;
5853
0
        break;
5854
0
    case ImGuiDir_None:
5855
0
    case ImGuiDir_COUNT:
5856
0
        IM_ASSERT(0);
5857
0
        break;
5858
80.5k
    }
5859
80.5k
    draw_list->AddTriangleFilled(center + a, center + b, center + c, col);
5860
80.5k
}
5861
5862
void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)
5863
0
{
5864
    // FIXME-OPT: This should be baked in font now that it's easier.
5865
0
    float font_size = draw_list->_Data->FontSize;
5866
0
    draw_list->AddCircleFilled(pos, font_size * 0.20f, col, (font_size < 22) ? 8 : (font_size < 40) ? 12 : 0); // Hardcode optimal/nice tessellation threshold
5867
0
}
5868
5869
void ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz)
5870
659
{
5871
659
    float thickness = ImMax(sz / 5.0f, 1.0f);
5872
659
    sz -= thickness * 0.5f;
5873
659
    pos += ImVec2(thickness * 0.25f, thickness * 0.25f);
5874
5875
659
    float third = sz / 3.0f;
5876
659
    float bx = pos.x + third;
5877
659
    float by = pos.y + sz - third * 0.5f;
5878
659
    draw_list->PathLineTo(ImVec2(bx - third, by - third));
5879
659
    draw_list->PathLineTo(ImVec2(bx, by));
5880
659
    draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f));
5881
659
    draw_list->PathStroke(col, 0, thickness);
5882
659
}
5883
5884
// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
5885
void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
5886
0
{
5887
0
    switch (direction)
5888
0
    {
5889
0
    case ImGuiDir_Left:  draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;
5890
0
    case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;
5891
0
    case ImGuiDir_Up:    draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;
5892
0
    case ImGuiDir_Down:  draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;
5893
0
    case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings
5894
0
    }
5895
0
}
5896
5897
static inline float ImAcos01(float x)
5898
0
{
5899
0
    if (x <= 0.0f) return IM_PI * 0.5f;
5900
0
    if (x >= 1.0f) return 0.0f;
5901
0
    return ImAcos(x);
5902
    //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do.
5903
0
}
5904
5905
// FIXME: Cleanup and move code to ImDrawList.
5906
void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding)
5907
0
{
5908
0
    if (x_end_norm == x_start_norm)
5909
0
        return;
5910
0
    if (x_start_norm > x_end_norm)
5911
0
        ImSwap(x_start_norm, x_end_norm);
5912
5913
0
    ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y);
5914
0
    ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y);
5915
0
    if (rounding == 0.0f)
5916
0
    {
5917
0
        draw_list->AddRectFilled(p0, p1, col, 0.0f);
5918
0
        return;
5919
0
    }
5920
5921
0
    rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding);
5922
0
    const float inv_rounding = 1.0f / rounding;
5923
0
    const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding);
5924
0
    const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding);
5925
0
    const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return.
5926
0
    const float x0 = ImMax(p0.x, rect.Min.x + rounding);
5927
0
    if (arc0_b == arc0_e)
5928
0
    {
5929
0
        draw_list->PathLineTo(ImVec2(x0, p1.y));
5930
0
        draw_list->PathLineTo(ImVec2(x0, p0.y));
5931
0
    }
5932
0
    else if (arc0_b == 0.0f && arc0_e == half_pi)
5933
0
    {
5934
0
        draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL
5935
0
        draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR
5936
0
    }
5937
0
    else
5938
0
    {
5939
0
        draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL
5940
0
        draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR
5941
0
    }
5942
0
    if (p1.x > rect.Min.x + rounding)
5943
0
    {
5944
0
        const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding);
5945
0
        const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding);
5946
0
        const float x1 = ImMin(p1.x, rect.Max.x - rounding);
5947
0
        if (arc1_b == arc1_e)
5948
0
        {
5949
0
            draw_list->PathLineTo(ImVec2(x1, p0.y));
5950
0
            draw_list->PathLineTo(ImVec2(x1, p1.y));
5951
0
        }
5952
0
        else if (arc1_b == 0.0f && arc1_e == half_pi)
5953
0
        {
5954
0
            draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR
5955
0
            draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3);  // BR
5956
0
        }
5957
0
        else
5958
0
        {
5959
0
            draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR
5960
0
            draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR
5961
0
        }
5962
0
    }
5963
0
    draw_list->PathFillConvex(col);
5964
0
}
5965
5966
void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding)
5967
0
{
5968
0
    const bool fill_L = (inner.Min.x > outer.Min.x);
5969
0
    const bool fill_R = (inner.Max.x < outer.Max.x);
5970
0
    const bool fill_U = (inner.Min.y > outer.Min.y);
5971
0
    const bool fill_D = (inner.Max.y < outer.Max.y);
5972
0
    if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft)    | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft));
5973
0
    if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight)   | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight));
5974
0
    if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft)    | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight));
5975
0
    if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight));
5976
0
    if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft);
5977
0
    if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight);
5978
0
    if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft);
5979
0
    if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);
5980
0
}
5981
5982
// Helper for ColorPicker4()
5983
// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.
5984
// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.
5985
// FIXME: uses ImGui::GetColorU32
5986
void ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags)
5987
0
{
5988
0
    if ((flags & ImDrawFlags_RoundCornersMask_) == 0)
5989
0
        flags = ImDrawFlags_RoundCornersDefault_;
5990
0
    if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)
5991
0
    {
5992
0
        ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col));
5993
0
        ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col));
5994
0
        draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags);
5995
5996
0
        int yi = 0;
5997
0
        for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)
5998
0
        {
5999
0
            float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);
6000
0
            if (y2 <= y1)
6001
0
                continue;
6002
0
            for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)
6003
0
            {
6004
0
                float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);
6005
0
                if (x2 <= x1)
6006
0
                    continue;
6007
0
                ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone;
6008
0
                if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; }
6009
0
                if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; }
6010
6011
                // Combine flags
6012
0
                cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags);
6013
0
                draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags);
6014
0
            }
6015
0
        }
6016
0
    }
6017
0
    else
6018
0
    {
6019
0
        draw_list->AddRectFilled(p_min, p_max, col, rounding, flags);
6020
0
    }
6021
0
}
6022
6023
//-----------------------------------------------------------------------------
6024
// [SECTION] Decompression code
6025
//-----------------------------------------------------------------------------
6026
// Compressed with stb_compress() then converted to a C array and encoded as base85.
6027
// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file.
6028
// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
6029
// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h
6030
//-----------------------------------------------------------------------------
6031
6032
static unsigned int stb_decompress_length(const unsigned char *input)
6033
2
{
6034
2
    return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11];
6035
2
}
6036
6037
static unsigned char *stb__barrier_out_e, *stb__barrier_out_b;
6038
static const unsigned char *stb__barrier_in_b;
6039
static unsigned char *stb__dout;
6040
static void stb__match(const unsigned char *data, unsigned int length)
6041
2.32k
{
6042
    // INVERSE of memmove... write each byte before copying the next...
6043
2.32k
    IM_ASSERT(stb__dout + length <= stb__barrier_out_e);
6044
2.32k
    if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; }
6045
2.32k
    if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; }
6046
40.8k
    while (length--) *stb__dout++ = *data++;
6047
2.32k
}
6048
6049
static void stb__lit(const unsigned char *data, unsigned int length)
6050
660
{
6051
660
    IM_ASSERT(stb__dout + length <= stb__barrier_out_e);
6052
660
    if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; }
6053
660
    if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; }
6054
660
    memcpy(stb__dout, data, length);
6055
660
    stb__dout += length;
6056
660
}
6057
6058
1.52k
#define stb__in2(x)   ((i[x] << 8) + i[(x)+1])
6059
114
#define stb__in3(x)   ((i[x] << 16) + stb__in2((x)+1))
6060
3
#define stb__in4(x)   ((i[x] << 24) + stb__in3((x)+1))
6061
6062
static const unsigned char *stb_decompress_token(const unsigned char *i)
6063
2.98k
{
6064
2.98k
    if (*i >= 0x20) { // use fewer if's for cases that expand small
6065
2.86k
        if (*i >= 0x80)       stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2;
6066
2.04k
        else if (*i >= 0x40)  stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3;
6067
653
        else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);
6068
2.86k
    } else { // more ifs for cases that expand large, since overhead is amortized
6069
119
        if (*i >= 0x18)       stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4;
6070
8
        else if (*i >= 0x10)  stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5;
6071
8
        else if (*i >= 0x08)  stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1);
6072
1
        else if (*i == 0x07)  stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1);
6073
1
        else if (*i == 0x06)  stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5;
6074
1
        else if (*i == 0x04)  stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6;
6075
119
    }
6076
2.98k
    return i;
6077
2.98k
}
6078
6079
static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen)
6080
1
{
6081
1
    const unsigned long ADLER_MOD = 65521;
6082
1
    unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
6083
1
    unsigned long blocklen = buflen % 5552;
6084
6085
1
    unsigned long i;
6086
9
    while (buflen) {
6087
5.15k
        for (i=0; i + 7 < blocklen; i += 8) {
6088
5.15k
            s1 += buffer[0], s2 += s1;
6089
5.15k
            s1 += buffer[1], s2 += s1;
6090
5.15k
            s1 += buffer[2], s2 += s1;
6091
5.15k
            s1 += buffer[3], s2 += s1;
6092
5.15k
            s1 += buffer[4], s2 += s1;
6093
5.15k
            s1 += buffer[5], s2 += s1;
6094
5.15k
            s1 += buffer[6], s2 += s1;
6095
5.15k
            s1 += buffer[7], s2 += s1;
6096
6097
5.15k
            buffer += 8;
6098
5.15k
        }
6099
6100
8
        for (; i < blocklen; ++i)
6101
0
            s1 += *buffer++, s2 += s1;
6102
6103
8
        s1 %= ADLER_MOD, s2 %= ADLER_MOD;
6104
8
        buflen -= blocklen;
6105
8
        blocklen = 5552;
6106
8
    }
6107
1
    return (unsigned int)(s2 << 16) + (unsigned int)s1;
6108
1
}
6109
6110
static unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/)
6111
1
{
6112
1
    if (stb__in4(0) != 0x57bC0000) return 0;
6113
1
    if (stb__in4(4) != 0)          return 0; // error! stream is > 4GB
6114
1
    const unsigned int olen = stb_decompress_length(i);
6115
1
    stb__barrier_in_b = i;
6116
1
    stb__barrier_out_e = output + olen;
6117
1
    stb__barrier_out_b = output;
6118
1
    i += 16;
6119
6120
1
    stb__dout = output;
6121
2.98k
    for (;;) {
6122
2.98k
        const unsigned char *old_i = i;
6123
2.98k
        i = stb_decompress_token(i);
6124
2.98k
        if (i == old_i) {
6125
1
            if (*i == 0x05 && i[1] == 0xfa) {
6126
1
                IM_ASSERT(stb__dout == output + olen);
6127
1
                if (stb__dout != output + olen) return 0;
6128
1
                if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2))
6129
0
                    return 0;
6130
1
                return olen;
6131
1
            } else {
6132
0
                IM_ASSERT(0); /* NOTREACHED */
6133
0
                return 0;
6134
0
            }
6135
1
        }
6136
2.98k
        IM_ASSERT(stb__dout <= output + olen);
6137
2.98k
        if (stb__dout > output + olen)
6138
0
            return 0;
6139
2.98k
    }
6140
1
}
6141
6142
//-----------------------------------------------------------------------------
6143
// [SECTION] Default font data (ProggyClean.ttf)
6144
//-----------------------------------------------------------------------------
6145
// ProggyClean.ttf
6146
// Copyright (c) 2004, 2005 Tristan Grimmer
6147
// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
6148
// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
6149
// If you want a similar font which may be better scaled, consider using ProggyVector from the same author!
6150
//-----------------------------------------------------------------------------
6151
6152
#ifndef IMGUI_DISABLE_DEFAULT_FONT
6153
6154
// File: 'ProggyClean.ttf' (41208 bytes)
6155
// Exported using binary_to_compressed_c.exe -u8 "ProggyClean.ttf" proggy_clean_ttf
6156
static const unsigned int proggy_clean_ttf_compressed_size = 9583;
6157
static const unsigned char proggy_clean_ttf_compressed_data[9583] =
6158
{
6159
    87,188,0,0,0,0,0,0,0,0,160,248,0,4,0,0,55,0,1,0,0,0,12,0,128,0,3,0,64,79,83,47,50,136,235,116,144,0,0,1,72,130,21,44,78,99,109,97,112,2,18,35,117,0,0,3,160,130,19,36,82,99,118,116,
6160
    32,130,23,130,2,33,4,252,130,4,56,2,103,108,121,102,18,175,137,86,0,0,7,4,0,0,146,128,104,101,97,100,215,145,102,211,130,27,32,204,130,3,33,54,104,130,16,39,8,66,1,195,0,0,1,4,130,
6161
    15,59,36,104,109,116,120,138,0,126,128,0,0,1,152,0,0,2,6,108,111,99,97,140,115,176,216,0,0,5,130,30,41,2,4,109,97,120,112,1,174,0,218,130,31,32,40,130,16,44,32,110,97,109,101,37,89,
6162
    187,150,0,0,153,132,130,19,44,158,112,111,115,116,166,172,131,239,0,0,155,36,130,51,44,210,112,114,101,112,105,2,1,18,0,0,4,244,130,47,32,8,132,203,46,1,0,0,60,85,233,213,95,15,60,
6163
    245,0,3,8,0,131,0,34,183,103,119,130,63,43,0,0,189,146,166,215,0,0,254,128,3,128,131,111,130,241,33,2,0,133,0,32,1,130,65,38,192,254,64,0,0,3,128,131,16,130,5,32,1,131,7,138,3,33,2,
6164
    0,130,17,36,1,1,0,144,0,130,121,130,23,38,2,0,8,0,64,0,10,130,9,32,118,130,9,130,6,32,0,130,59,33,1,144,131,200,35,2,188,2,138,130,16,32,143,133,7,37,1,197,0,50,2,0,131,0,33,4,9,131,
6165
    5,145,3,43,65,108,116,115,0,64,0,0,32,172,8,0,131,0,35,5,0,1,128,131,77,131,3,33,3,128,191,1,33,1,128,130,184,35,0,0,128,0,130,3,131,11,32,1,130,7,33,0,128,131,1,32,1,136,9,32,0,132,
6166
    15,135,5,32,1,131,13,135,27,144,35,32,1,149,25,131,21,32,0,130,0,32,128,132,103,130,35,132,39,32,0,136,45,136,97,133,17,130,5,33,0,0,136,19,34,0,128,1,133,13,133,5,32,128,130,15,132,
6167
    131,32,3,130,5,32,3,132,27,144,71,32,0,133,27,130,29,130,31,136,29,131,63,131,3,65,63,5,132,5,132,205,130,9,33,0,0,131,9,137,119,32,3,132,19,138,243,130,55,32,1,132,35,135,19,131,201,
6168
    136,11,132,143,137,13,130,41,32,0,131,3,144,35,33,128,0,135,1,131,223,131,3,141,17,134,13,136,63,134,15,136,53,143,15,130,96,33,0,3,131,4,130,3,34,28,0,1,130,5,34,0,0,76,130,17,131,
6169
    9,36,28,0,4,0,48,130,17,46,8,0,8,0,2,0,0,0,127,0,255,32,172,255,255,130,9,34,0,0,129,132,9,130,102,33,223,213,134,53,132,22,33,1,6,132,6,64,4,215,32,129,165,216,39,177,0,1,141,184,
6170
    1,255,133,134,45,33,198,0,193,1,8,190,244,1,28,1,158,2,20,2,136,2,252,3,20,3,88,3,156,3,222,4,20,4,50,4,80,4,98,4,162,5,22,5,102,5,188,6,18,6,116,6,214,7,56,7,126,7,236,8,78,8,108,
6171
    8,150,8,208,9,16,9,74,9,136,10,22,10,128,11,4,11,86,11,200,12,46,12,130,12,234,13,94,13,164,13,234,14,80,14,150,15,40,15,176,16,18,16,116,16,224,17,82,17,182,18,4,18,110,18,196,19,
6172
    76,19,172,19,246,20,88,20,174,20,234,21,64,21,128,21,166,21,184,22,18,22,126,22,198,23,52,23,142,23,224,24,86,24,186,24,238,25,54,25,150,25,212,26,72,26,156,26,240,27,92,27,200,28,
6173
    4,28,76,28,150,28,234,29,42,29,146,29,210,30,64,30,142,30,224,31,36,31,118,31,166,31,166,32,16,130,1,52,46,32,138,32,178,32,200,33,20,33,116,33,152,33,238,34,98,34,134,35,12,130,1,
6174
    33,128,35,131,1,60,152,35,176,35,216,36,0,36,74,36,104,36,144,36,174,37,6,37,96,37,130,37,248,37,248,38,88,38,170,130,1,8,190,216,39,64,39,154,40,10,40,104,40,168,41,14,41,32,41,184,
6175
    41,248,42,54,42,96,42,96,43,2,43,42,43,94,43,172,43,230,44,32,44,52,44,154,45,40,45,92,45,120,45,170,45,232,46,38,46,166,47,38,47,182,47,244,48,94,48,200,49,62,49,180,50,30,50,158,
6176
    51,30,51,130,51,238,52,92,52,206,53,58,53,134,53,212,54,38,54,114,54,230,55,118,55,216,56,58,56,166,57,18,57,116,57,174,58,46,58,154,59,6,59,124,59,232,60,58,60,150,61,34,61,134,61,
6177
    236,62,86,62,198,63,42,63,154,64,18,64,106,64,208,65,54,65,162,66,8,66,64,66,122,66,184,66,240,67,98,67,204,68,42,68,138,68,238,69,88,69,182,69,226,70,84,70,180,71,20,71,122,71,218,
6178
    72,84,72,198,73,64,0,36,70,21,8,8,77,3,0,7,0,11,0,15,0,19,0,23,0,27,0,31,0,35,0,39,0,43,0,47,0,51,0,55,0,59,0,63,0,67,0,71,0,75,0,79,0,83,0,87,0,91,0,95,0,99,0,103,0,107,0,111,0,115,
6179
    0,119,0,123,0,127,0,131,0,135,0,139,0,143,0,0,17,53,51,21,49,150,3,32,5,130,23,32,33,130,3,211,7,151,115,32,128,133,0,37,252,128,128,2,128,128,190,5,133,74,32,4,133,6,206,5,42,0,7,
6180
    1,128,0,0,2,0,4,0,0,65,139,13,37,0,1,53,51,21,7,146,3,32,3,130,19,32,1,141,133,32,3,141,14,131,13,38,255,0,128,128,0,6,1,130,84,35,2,128,4,128,140,91,132,89,32,51,65,143,6,139,7,33,
6181
    1,0,130,57,32,254,130,3,32,128,132,4,32,4,131,14,138,89,35,0,0,24,0,130,0,33,3,128,144,171,66,55,33,148,115,65,187,19,32,5,130,151,143,155,163,39,32,1,136,182,32,253,134,178,132,7,
6182
    132,200,145,17,32,3,65,48,17,165,17,39,0,0,21,0,128,255,128,3,65,175,17,65,3,27,132,253,131,217,139,201,155,233,155,27,131,67,131,31,130,241,33,255,0,131,181,137,232,132,15,132,4,138,
6183
    247,34,255,0,128,179,238,32,0,130,0,32,20,65,239,48,33,0,19,67,235,10,32,51,65,203,14,65,215,11,32,7,154,27,135,39,32,33,130,35,33,128,128,130,231,32,253,132,231,32,128,132,232,34,
6184
    128,128,254,133,13,136,8,32,253,65,186,5,130,36,130,42,176,234,133,231,34,128,0,0,66,215,44,33,0,1,68,235,6,68,211,19,32,49,68,239,14,139,207,139,47,66,13,7,32,51,130,47,33,1,0,130,
6185
    207,35,128,128,1,0,131,222,131,5,130,212,130,6,131,212,32,0,130,10,133,220,130,233,130,226,32,254,133,255,178,233,39,3,1,128,3,0,2,0,4,68,15,7,68,99,12,130,89,130,104,33,128,4,133,
6186
    93,130,10,38,0,0,11,1,0,255,0,68,63,16,70,39,9,66,215,8,32,7,68,77,6,68,175,14,32,29,68,195,6,132,7,35,2,0,128,255,131,91,132,4,65,178,5,141,111,67,129,23,165,135,140,107,142,135,33,
6187
    21,5,69,71,6,131,7,33,1,0,140,104,132,142,130,4,137,247,140,30,68,255,12,39,11,0,128,0,128,3,0,3,69,171,15,67,251,7,65,15,8,66,249,11,65,229,7,67,211,7,66,13,7,35,1,128,128,254,133,
6188
    93,32,254,131,145,132,4,132,18,32,2,151,128,130,23,34,0,0,9,154,131,65,207,8,68,107,15,68,51,7,32,7,70,59,7,135,121,130,82,32,128,151,111,41,0,0,4,0,128,255,0,1,128,1,137,239,33,0,
6189
    37,70,145,10,65,77,10,65,212,14,37,0,0,0,5,0,128,66,109,5,70,123,10,33,0,19,72,33,18,133,237,70,209,11,33,0,2,130,113,137,119,136,115,33,1,0,133,43,130,5,34,0,0,10,69,135,6,70,219,
6190
    13,66,155,7,65,9,12,66,157,11,66,9,11,32,7,130,141,132,252,66,151,9,137,9,66,15,30,36,0,20,0,128,0,130,218,71,11,42,68,51,8,65,141,7,73,19,15,69,47,23,143,39,66,81,7,32,1,66,55,6,34,
6191
    1,128,128,68,25,5,69,32,6,137,6,136,25,32,254,131,42,32,3,66,88,26,148,26,32,0,130,0,32,14,164,231,70,225,12,66,233,7,67,133,19,71,203,15,130,161,32,255,130,155,32,254,139,127,134,
6192
    12,164,174,33,0,15,164,159,33,59,0,65,125,20,66,25,7,32,5,68,191,6,66,29,7,144,165,65,105,9,35,128,128,255,0,137,2,133,182,164,169,33,128,128,197,171,130,155,68,235,7,32,21,70,77,19,
6193
    66,21,10,68,97,8,66,30,5,66,4,43,34,0,17,0,71,19,41,65,253,20,71,25,23,65,91,15,65,115,7,34,2,128,128,66,9,8,130,169,33,1,0,66,212,13,132,28,72,201,43,35,0,0,0,18,66,27,38,76,231,5,
6194
    68,157,20,135,157,32,7,68,185,13,65,129,28,66,20,5,32,253,66,210,11,65,128,49,133,61,32,0,65,135,6,74,111,37,72,149,12,66,203,19,65,147,19,68,93,7,68,85,8,76,4,5,33,255,0,133,129,34,
6195
    254,0,128,68,69,8,181,197,34,0,0,12,65,135,32,65,123,20,69,183,27,133,156,66,50,5,72,87,10,67,137,32,33,0,19,160,139,78,251,13,68,55,20,67,119,19,65,91,36,69,177,15,32,254,143,16,65,
6196
    98,53,32,128,130,0,32,0,66,43,54,70,141,23,66,23,15,131,39,69,47,11,131,15,70,129,19,74,161,9,36,128,255,0,128,254,130,153,65,148,32,67,41,9,34,0,0,4,79,15,5,73,99,10,71,203,8,32,3,
6197
    72,123,6,72,43,8,32,2,133,56,131,99,130,9,34,0,0,6,72,175,5,73,159,14,144,63,135,197,132,189,133,66,33,255,0,73,6,7,70,137,12,35,0,0,0,10,130,3,73,243,25,67,113,12,65,73,7,69,161,7,
6198
    138,7,37,21,2,0,128,128,254,134,3,73,116,27,33,128,128,130,111,39,12,0,128,1,0,3,128,2,72,219,21,35,43,0,47,0,67,47,20,130,111,33,21,1,68,167,13,81,147,8,133,230,32,128,77,73,6,32,
6199
    128,131,142,134,18,130,6,32,255,75,18,12,131,243,37,128,0,128,3,128,3,74,231,21,135,123,32,29,134,107,135,7,32,21,74,117,7,135,7,134,96,135,246,74,103,23,132,242,33,0,10,67,151,28,
6200
    67,133,20,66,141,11,131,11,32,3,77,71,6,32,128,130,113,32,1,81,4,6,134,218,66,130,24,131,31,34,0,26,0,130,0,77,255,44,83,15,11,148,155,68,13,7,32,49,78,231,18,79,7,11,73,243,11,32,
6201
    33,65,187,10,130,63,65,87,8,73,239,19,35,0,128,1,0,131,226,32,252,65,100,6,32,128,139,8,33,1,0,130,21,32,253,72,155,44,73,255,20,32,128,71,67,8,81,243,39,67,15,20,74,191,23,68,121,
6202
    27,32,1,66,150,6,32,254,79,19,11,131,214,32,128,130,215,37,2,0,128,253,0,128,136,5,65,220,24,147,212,130,210,33,0,24,72,219,42,84,255,13,67,119,16,69,245,19,72,225,19,65,3,15,69,93,
6203
    19,131,55,132,178,71,115,14,81,228,6,142,245,33,253,0,132,43,172,252,65,16,11,75,219,8,65,219,31,66,223,24,75,223,10,33,29,1,80,243,10,66,175,8,131,110,134,203,133,172,130,16,70,30,
6204
    7,164,183,130,163,32,20,65,171,48,65,163,36,65,143,23,65,151,19,65,147,13,65,134,17,133,17,130,216,67,114,5,164,217,65,137,12,72,147,48,79,71,19,74,169,22,80,251,8,65,173,7,66,157,
6205
    15,74,173,15,32,254,65,170,8,71,186,45,72,131,6,77,143,40,187,195,152,179,65,123,38,68,215,57,68,179,15,65,85,7,69,187,14,32,21,66,95,15,67,19,25,32,1,83,223,6,32,2,76,240,7,77,166,
6206
    43,65,8,5,130,206,32,0,67,39,54,143,167,66,255,19,82,193,11,151,47,85,171,5,67,27,17,132,160,69,172,11,69,184,56,66,95,6,33,12,1,130,237,32,2,68,179,27,68,175,16,80,135,15,72,55,7,
6207
    71,87,12,73,3,12,132,12,66,75,32,76,215,5,169,139,147,135,148,139,81,12,12,81,185,36,75,251,7,65,23,27,76,215,9,87,165,12,65,209,15,72,157,7,65,245,31,32,128,71,128,6,32,1,82,125,5,
6208
    34,0,128,254,131,169,32,254,131,187,71,180,9,132,27,32,2,88,129,44,32,0,78,47,40,65,79,23,79,171,14,32,21,71,87,8,72,15,14,65,224,33,130,139,74,27,62,93,23,7,68,31,7,75,27,7,139,15,
6209
    74,3,7,74,23,27,65,165,11,65,177,15,67,123,5,32,1,130,221,32,252,71,96,5,74,12,12,133,244,130,25,34,1,0,128,130,2,139,8,93,26,8,65,9,32,65,57,14,140,14,32,0,73,79,67,68,119,11,135,
6210
    11,32,51,90,75,14,139,247,65,43,7,131,19,139,11,69,159,11,65,247,6,36,1,128,128,253,0,90,71,9,33,1,0,132,14,32,128,89,93,14,69,133,6,130,44,131,30,131,6,65,20,56,33,0,16,72,179,40,
6211
    75,47,12,65,215,19,74,95,19,65,43,11,131,168,67,110,5,75,23,17,69,106,6,75,65,5,71,204,43,32,0,80,75,47,71,203,15,159,181,68,91,11,67,197,7,73,101,13,68,85,6,33,128,128,130,214,130,
6212
    25,32,254,74,236,48,130,194,37,0,18,0,128,255,128,77,215,40,65,139,64,32,51,80,159,10,65,147,39,130,219,84,212,43,130,46,75,19,97,74,33,11,65,201,23,65,173,31,33,1,0,79,133,6,66,150,
6213
    5,67,75,48,85,187,6,70,207,37,32,71,87,221,13,73,163,14,80,167,15,132,15,83,193,19,82,209,8,78,99,9,72,190,11,77,110,49,89,63,5,80,91,35,99,63,32,70,235,23,81,99,10,69,148,10,65,110,
6214
    36,32,0,65,99,47,95,219,11,68,171,51,66,87,7,72,57,7,74,45,17,143,17,65,114,50,33,14,0,65,111,40,159,195,98,135,15,35,7,53,51,21,100,78,9,95,146,16,32,254,82,114,6,32,128,67,208,37,
6215
    130,166,99,79,58,32,17,96,99,14,72,31,19,72,87,31,82,155,7,67,47,14,32,21,131,75,134,231,72,51,17,72,78,8,133,8,80,133,6,33,253,128,88,37,9,66,124,36,72,65,12,134,12,71,55,43,66,139,
6216
    27,85,135,10,91,33,12,65,35,11,66,131,11,71,32,8,90,127,6,130,244,71,76,11,168,207,33,0,12,66,123,32,32,0,65,183,15,68,135,11,66,111,7,67,235,11,66,111,15,32,254,97,66,12,160,154,67,
6217
    227,52,80,33,15,87,249,15,93,45,31,75,111,12,93,45,11,77,99,9,160,184,81,31,12,32,15,98,135,30,104,175,7,77,249,36,69,73,15,78,5,12,32,254,66,151,19,34,128,128,4,87,32,12,149,35,133,
6218
    21,96,151,31,32,19,72,35,5,98,173,15,143,15,32,21,143,99,158,129,33,0,0,65,35,52,65,11,15,147,15,98,75,11,33,1,0,143,151,132,15,32,254,99,200,37,132,43,130,4,39,0,10,0,128,1,128,3,
6219
    0,104,151,14,97,187,20,69,131,15,67,195,11,87,227,7,33,128,128,132,128,33,254,0,68,131,9,65,46,26,42,0,0,0,7,0,0,255,128,3,128,0,88,223,15,33,0,21,89,61,22,66,209,12,65,2,12,37,0,2,
6220
    1,0,3,128,101,83,8,36,0,1,53,51,29,130,3,34,21,1,0,66,53,8,32,0,68,215,6,100,55,25,107,111,9,66,193,11,72,167,8,73,143,31,139,31,33,1,0,131,158,32,254,132,5,33,253,128,65,16,9,133,
6221
    17,89,130,25,141,212,33,0,0,93,39,8,90,131,25,93,39,14,66,217,6,106,179,8,159,181,71,125,15,139,47,138,141,87,11,14,76,23,14,65,231,26,140,209,66,122,8,81,179,5,101,195,26,32,47,74,
6222
    75,13,69,159,11,83,235,11,67,21,16,136,167,131,106,130,165,130,15,32,128,101,90,24,134,142,32,0,65,103,51,108,23,11,101,231,15,75,173,23,74,237,23,66,15,6,66,46,17,66,58,17,65,105,
6223
    49,66,247,55,71,179,12,70,139,15,86,229,7,84,167,15,32,1,95,72,12,89,49,6,33,128,128,65,136,38,66,30,9,32,0,100,239,7,66,247,29,70,105,20,65,141,19,69,81,15,130,144,32,128,83,41,5,
6224
    32,255,131,177,68,185,5,133,126,65,97,37,32,0,130,0,33,21,0,130,55,66,195,28,67,155,13,34,79,0,83,66,213,13,73,241,19,66,59,19,65,125,11,135,201,66,249,16,32,128,66,44,11,66,56,17,
6225
    68,143,8,68,124,38,67,183,12,96,211,9,65,143,29,112,171,5,32,0,68,131,63,34,33,53,51,71,121,11,32,254,98,251,16,32,253,74,231,10,65,175,37,133,206,37,0,0,8,1,0,0,107,123,11,113,115,
6226
    9,33,0,1,130,117,131,3,73,103,7,66,51,18,66,44,5,133,75,70,88,5,32,254,65,39,12,68,80,9,34,12,0,128,107,179,28,68,223,6,155,111,86,147,15,32,2,131,82,141,110,33,254,0,130,15,32,4,103,
6227
    184,15,141,35,87,176,5,83,11,5,71,235,23,114,107,11,65,189,16,70,33,15,86,153,31,135,126,86,145,30,65,183,41,32,0,130,0,32,10,65,183,24,34,35,0,39,67,85,9,65,179,15,143,15,33,1,0,65,
6228
    28,17,157,136,130,123,32,20,130,3,32,0,97,135,24,115,167,19,80,71,12,32,51,110,163,14,78,35,19,131,19,155,23,77,229,8,78,9,17,151,17,67,231,46,94,135,8,73,31,31,93,215,56,82,171,25,
6229
    72,77,8,162,179,169,167,99,131,11,69,85,19,66,215,15,76,129,13,68,115,22,72,79,35,67,113,5,34,0,0,19,70,31,46,65,89,52,73,223,15,85,199,33,95,33,8,132,203,73,29,32,67,48,16,177,215,
6230
    101,13,15,65,141,43,69,141,15,75,89,5,70,0,11,70,235,21,178,215,36,10,0,128,0,0,71,207,24,33,0,19,100,67,6,80,215,11,66,67,7,80,43,12,71,106,7,80,192,5,65,63,5,66,217,26,33,0,13,156,
6231
    119,68,95,5,72,233,12,134,129,85,81,11,76,165,20,65,43,8,73,136,8,75,10,31,38,128,128,0,0,0,13,1,130,4,32,3,106,235,29,114,179,12,66,131,23,32,7,77,133,6,67,89,12,131,139,116,60,9,
6232
    89,15,37,32,0,74,15,7,103,11,22,65,35,5,33,55,0,93,81,28,67,239,23,78,85,5,107,93,14,66,84,17,65,193,26,74,183,10,66,67,34,143,135,79,91,15,32,7,117,111,8,75,56,9,84,212,9,154,134,
6233
    32,0,130,0,32,18,130,3,70,171,41,83,7,16,70,131,19,84,191,15,84,175,19,84,167,30,84,158,12,154,193,68,107,15,33,0,0,65,79,42,65,71,7,73,55,7,118,191,16,83,180,9,32,255,76,166,9,154,
6234
    141,32,0,130,0,69,195,52,65,225,15,151,15,75,215,31,80,56,10,68,240,17,100,32,9,70,147,39,65,93,12,71,71,41,92,85,15,84,135,23,78,35,15,110,27,10,84,125,8,107,115,29,136,160,38,0,0,
6235
    14,0,128,255,0,82,155,24,67,239,8,119,255,11,69,131,11,77,29,6,112,31,8,134,27,105,203,8,32,2,75,51,11,75,195,12,74,13,29,136,161,37,128,0,0,0,11,1,130,163,82,115,8,125,191,17,69,35,
6236
    12,74,137,15,143,15,32,1,65,157,12,136,12,161,142,65,43,40,65,199,6,65,19,24,102,185,11,76,123,11,99,6,12,135,12,32,254,130,8,161,155,101,23,9,39,8,0,0,1,128,3,128,2,78,63,17,72,245,
6237
    12,67,41,11,90,167,9,32,128,97,49,9,32,128,109,51,14,132,97,81,191,8,130,97,125,99,12,121,35,9,127,75,15,71,79,12,81,151,23,87,97,7,70,223,15,80,245,16,105,97,15,32,254,113,17,6,32,
6238
    128,130,8,105,105,8,76,122,18,65,243,21,74,63,7,38,4,1,0,255,0,2,0,119,247,28,133,65,32,255,141,91,35,0,0,0,16,67,63,36,34,59,0,63,77,59,9,119,147,11,143,241,66,173,15,66,31,11,67,
6239
    75,8,81,74,16,32,128,131,255,87,181,42,127,43,5,34,255,128,2,120,235,11,37,19,0,23,0,0,37,109,191,14,118,219,7,127,43,14,65,79,14,35,0,0,0,3,73,91,5,130,5,38,3,0,7,0,11,0,0,70,205,
6240
    11,88,221,12,32,0,73,135,7,87,15,22,73,135,10,79,153,15,97,71,19,65,49,11,32,1,131,104,121,235,11,80,65,11,142,179,144,14,81,123,46,32,1,88,217,5,112,5,8,65,201,15,83,29,15,122,147,
6241
    11,135,179,142,175,143,185,67,247,39,66,199,7,35,5,0,128,3,69,203,15,123,163,12,67,127,7,130,119,71,153,10,141,102,70,175,8,32,128,121,235,30,136,89,100,191,11,116,195,11,111,235,15,
6242
    72,39,7,32,2,97,43,5,132,5,94,67,8,131,8,125,253,10,32,3,65,158,16,146,16,130,170,40,0,21,0,128,0,0,3,128,5,88,219,15,24,64,159,32,135,141,65,167,15,68,163,10,97,73,49,32,255,82,58,
6243
    7,93,80,8,97,81,16,24,67,87,52,34,0,0,5,130,231,33,128,2,80,51,13,65,129,8,113,61,6,132,175,65,219,5,130,136,77,152,17,32,0,95,131,61,70,215,6,33,21,51,90,53,10,78,97,23,105,77,31,
6244
    65,117,7,139,75,24,68,195,9,24,64,22,9,33,0,128,130,11,33,128,128,66,25,5,121,38,5,134,5,134,45,66,40,36,66,59,18,34,128,0,0,66,59,81,135,245,123,103,19,120,159,19,77,175,12,33,255,
6245
    0,87,29,10,94,70,21,66,59,54,39,3,1,128,3,0,2,128,4,24,65,7,15,66,47,7,72,98,12,37,0,0,0,3,1,0,24,65,55,21,131,195,32,1,67,178,6,33,4,0,77,141,8,32,6,131,47,74,67,16,24,69,3,20,24,
6246
    65,251,7,133,234,130,229,94,108,17,35,0,0,6,0,141,175,86,59,5,162,79,85,166,8,70,112,13,32,13,24,64,67,26,24,71,255,7,123,211,12,80,121,11,69,215,15,66,217,11,69,71,10,131,113,132,
6247
    126,119,90,9,66,117,19,132,19,32,0,130,0,24,64,47,59,33,7,0,73,227,5,68,243,15,85,13,12,76,37,22,74,254,15,130,138,33,0,4,65,111,6,137,79,65,107,16,32,1,77,200,6,34,128,128,3,75,154,
6248
    12,37,0,16,0,0,2,0,104,115,36,140,157,68,67,19,68,51,15,106,243,15,134,120,70,37,10,68,27,10,140,152,65,121,24,32,128,94,155,7,67,11,8,24,74,11,25,65,3,12,83,89,18,82,21,37,67,200,
6249
    5,130,144,24,64,172,12,33,4,0,134,162,74,80,14,145,184,32,0,130,0,69,251,20,32,19,81,243,5,82,143,8,33,5,53,89,203,5,133,112,79,109,15,33,0,21,130,71,80,175,41,36,75,0,79,0,83,121,
6250
    117,9,87,89,27,66,103,11,70,13,15,75,191,11,135,67,87,97,20,109,203,5,69,246,8,108,171,5,78,195,38,65,51,13,107,203,11,77,3,17,24,75,239,17,65,229,28,79,129,39,130,175,32,128,123,253,
6251
    7,132,142,24,65,51,15,65,239,41,36,128,128,0,0,13,65,171,5,66,163,28,136,183,118,137,11,80,255,15,67,65,7,74,111,8,32,0,130,157,32,253,24,76,35,10,103,212,5,81,175,9,69,141,7,66,150,
6252
    29,131,158,24,75,199,28,124,185,7,76,205,15,68,124,14,32,3,123,139,16,130,16,33,128,128,108,199,6,33,0,3,65,191,35,107,11,6,73,197,11,24,70,121,15,83,247,15,24,70,173,23,69,205,14,
6253
    32,253,131,140,32,254,136,4,94,198,9,32,3,78,4,13,66,127,13,143,13,32,0,130,0,33,16,0,24,69,59,39,109,147,12,76,253,19,24,69,207,15,69,229,15,130,195,71,90,10,139,10,130,152,73,43,
6254
    40,91,139,10,65,131,37,35,75,0,79,0,84,227,12,143,151,68,25,15,80,9,23,95,169,11,34,128,2,128,112,186,5,130,6,83,161,19,76,50,6,130,37,65,145,44,110,83,5,32,16,67,99,6,71,67,15,76,
6255
    55,17,140,215,67,97,23,76,69,15,77,237,11,104,211,23,77,238,11,65,154,43,33,0,10,83,15,28,83,13,20,67,145,19,67,141,14,97,149,21,68,9,15,86,251,5,66,207,5,66,27,37,82,1,23,127,71,12,
6256
    94,235,10,110,175,24,98,243,15,132,154,132,4,24,66,69,10,32,4,67,156,43,130,198,35,2,1,0,4,75,27,9,69,85,9,95,240,7,32,128,130,35,32,28,66,43,40,24,82,63,23,83,123,12,72,231,15,127,
6257
    59,23,116,23,19,117,71,7,24,77,99,15,67,111,15,71,101,8,36,2,128,128,252,128,127,60,11,32,1,132,16,130,18,141,24,67,107,9,32,3,68,194,15,175,15,38,0,11,0,128,1,128,2,80,63,25,32,0,
6258
    24,65,73,11,69,185,15,83,243,16,32,0,24,81,165,8,130,86,77,35,6,155,163,88,203,5,24,66,195,30,70,19,19,24,80,133,15,32,1,75,211,8,32,254,108,133,8,79,87,20,65,32,9,41,0,0,7,0,128,0,
6259
    0,2,128,2,68,87,15,66,1,16,92,201,16,24,76,24,17,133,17,34,128,0,30,66,127,64,34,115,0,119,73,205,9,66,43,11,109,143,15,24,79,203,11,90,143,15,131,15,155,31,65,185,15,86,87,11,35,128,
6260
    128,253,0,69,7,6,130,213,33,1,0,119,178,15,142,17,66,141,74,83,28,6,36,7,0,0,4,128,82,39,18,76,149,12,67,69,21,32,128,79,118,15,32,0,130,0,32,8,131,206,32,2,79,83,9,100,223,14,102,
6261
    113,23,115,115,7,24,65,231,12,130,162,32,4,68,182,19,130,102,93,143,8,69,107,29,24,77,255,12,143,197,72,51,7,76,195,15,132,139,85,49,15,130,152,131,18,71,81,23,70,14,11,36,0,10,0,128,
6262
    2,69,59,9,89,151,15,66,241,11,76,165,12,71,43,15,75,49,13,65,12,23,132,37,32,0,179,115,130,231,95,181,16,132,77,32,254,67,224,8,65,126,20,79,171,8,32,2,89,81,5,75,143,6,80,41,8,34,
6263
    2,0,128,24,81,72,9,32,0,130,0,35,17,0,0,255,77,99,39,95,65,36,67,109,15,24,69,93,11,77,239,5,95,77,23,35,128,1,0,128,24,86,7,8,132,167,32,2,69,198,41,130,202,33,0,26,120,75,44,24,89,
6264
    51,15,71,243,12,70,239,11,24,84,3,11,66,7,11,71,255,10,32,21,69,155,35,88,151,12,32,128,74,38,10,65,210,8,74,251,5,65,226,5,75,201,13,32,3,65,9,41,146,41,40,0,0,0,9,1,0,1,0,2,91,99,
6265
    19,32,35,106,119,13,70,219,15,83,239,12,137,154,32,2,67,252,19,36,128,0,0,4,1,130,196,32,2,130,8,91,107,8,32,0,135,81,24,73,211,8,132,161,73,164,13,36,0,8,0,128,2,105,123,26,139,67,
6266
    76,99,15,34,1,0,128,135,76,83,156,20,92,104,8,67,251,30,24,86,47,27,123,207,12,24,86,7,15,71,227,8,32,4,65,20,20,131,127,32,0,130,123,32,0,71,223,26,32,19,90,195,22,71,223,15,84,200,
6267
    6,32,128,133,241,24,84,149,9,67,41,25,36,0,0,0,22,0,88,111,49,32,87,66,21,5,77,3,27,123,75,7,71,143,19,135,183,71,183,19,130,171,74,252,5,131,5,89,87,17,32,1,132,18,130,232,68,11,10,
6268
    33,1,128,70,208,16,66,230,18,147,18,130,254,223,255,75,27,23,65,59,15,135,39,155,255,34,128,128,254,104,92,8,33,0,128,65,32,11,65,1,58,33,26,0,130,0,72,71,18,78,55,17,76,11,19,86,101,
6269
    12,75,223,11,89,15,11,24,76,87,15,75,235,15,131,15,72,95,7,85,71,11,72,115,11,73,64,6,34,1,128,128,66,215,9,34,128,254,128,134,14,33,128,255,67,102,5,32,0,130,16,70,38,11,66,26,57,
6270
    88,11,8,24,76,215,34,78,139,7,95,245,7,32,7,24,73,75,23,32,128,131,167,130,170,101,158,9,82,49,22,118,139,6,32,18,67,155,44,116,187,9,108,55,14,80,155,23,66,131,15,93,77,10,131,168,
6271
    32,128,73,211,12,24,75,187,22,32,4,96,71,20,67,108,19,132,19,120,207,8,32,5,76,79,15,66,111,21,66,95,8,32,3,190,211,111,3,8,211,212,32,20,65,167,44,34,75,0,79,97,59,13,32,33,112,63,
6272
    10,65,147,19,69,39,19,143,39,24,66,71,9,130,224,65,185,43,94,176,12,65,183,24,71,38,8,24,72,167,7,65,191,38,136,235,24,96,167,12,65,203,62,115,131,13,65,208,42,175,235,67,127,6,32,
6273
    4,76,171,29,114,187,5,32,71,65,211,5,65,203,68,72,51,8,164,219,32,0,172,214,71,239,58,78,3,27,66,143,15,77,19,15,147,31,35,33,53,51,21,66,183,10,173,245,66,170,30,150,30,34,0,0,23,
6274
    80,123,54,76,1,16,73,125,15,82,245,11,167,253,24,76,85,12,70,184,5,32,254,131,185,37,254,0,128,1,0,128,133,16,117,158,18,92,27,38,65,3,17,130,251,35,17,0,128,254,24,69,83,39,140,243,
6275
    121,73,19,109,167,7,81,41,15,24,95,175,12,102,227,15,121,96,11,24,95,189,7,32,3,145,171,154,17,24,77,47,9,33,0,5,70,71,37,68,135,7,32,29,117,171,11,69,87,15,24,79,97,19,24,79,149,23,
6276
    131,59,32,1,75,235,5,72,115,11,72,143,7,132,188,71,27,46,131,51,32,0,69,95,6,175,215,32,21,131,167,81,15,19,151,191,151,23,131,215,71,43,5,32,254,24,79,164,24,74,109,8,77,166,13,65,
6277
    176,26,88,162,5,98,159,6,171,219,120,247,6,79,29,8,99,169,10,103,59,19,65,209,35,131,35,91,25,19,112,94,15,83,36,8,173,229,33,20,0,88,75,43,71,31,12,65,191,71,33,1,0,130,203,32,254,
6278
    131,4,68,66,7,67,130,6,104,61,13,173,215,38,13,1,0,0,0,2,128,67,111,28,74,129,16,104,35,19,79,161,16,87,14,7,138,143,132,10,67,62,36,114,115,5,162,151,67,33,16,108,181,15,143,151,67,
6279
    5,5,24,100,242,15,170,153,34,0,0,14,65,51,34,32,55,79,75,9,32,51,74,7,10,65,57,38,132,142,32,254,72,0,14,139,163,32,128,80,254,8,67,158,21,65,63,7,32,4,72,227,27,95,155,12,67,119,19,
6280
    124,91,24,149,154,72,177,34,97,223,8,155,151,24,108,227,15,88,147,16,72,117,19,68,35,11,92,253,15,70,199,15,24,87,209,17,32,2,87,233,7,32,1,24,88,195,10,119,24,8,32,3,81,227,24,65,
6281
    125,21,35,128,128,0,25,76,59,48,24,90,187,9,97,235,12,66,61,11,91,105,19,24,79,141,11,24,79,117,15,24,79,129,27,90,53,13,130,13,32,253,131,228,24,79,133,40,69,70,8,66,137,31,65,33,
6282
    19,96,107,8,68,119,29,66,7,5,68,125,16,65,253,19,65,241,27,24,90,179,13,24,79,143,18,33,128,128,130,246,32,254,130,168,68,154,36,77,51,9,97,47,5,167,195,32,21,131,183,78,239,27,155,
6283
    195,78,231,14,201,196,77,11,6,32,5,73,111,37,97,247,12,77,19,31,155,207,78,215,19,162,212,69,17,14,66,91,19,80,143,57,78,203,39,159,215,32,128,93,134,8,24,80,109,24,66,113,15,169,215,
6284
    66,115,6,32,4,69,63,33,32,0,101,113,7,86,227,35,143,211,36,49,53,51,21,1,77,185,14,65,159,28,69,251,34,67,56,8,33,9,0,24,107,175,25,90,111,12,110,251,11,119,189,24,119,187,34,87,15,
6285
    9,32,4,66,231,37,90,39,7,66,239,8,84,219,15,69,105,23,24,85,27,27,87,31,11,33,1,128,76,94,6,32,1,85,241,7,33,128,128,106,48,10,33,128,128,69,136,11,133,13,24,79,116,49,84,236,8,24,
6286
    91,87,9,32,5,165,255,69,115,12,66,27,15,159,15,24,72,247,12,74,178,5,24,80,64,15,33,0,128,143,17,77,89,51,130,214,24,81,43,7,170,215,74,49,8,159,199,143,31,139,215,69,143,5,32,254,
6287
    24,81,50,35,181,217,84,123,70,143,195,159,15,65,187,16,66,123,7,65,175,15,65,193,29,68,207,39,79,27,5,70,131,6,32,4,68,211,33,33,67,0,83,143,14,159,207,143,31,140,223,33,0,128,24,80,
6288
    82,14,24,93,16,23,32,253,65,195,5,68,227,40,133,214,107,31,7,32,5,67,115,27,87,9,8,107,31,43,66,125,6,32,0,103,177,23,131,127,72,203,36,32,0,110,103,8,155,163,73,135,6,32,19,24,112,
6289
    99,10,65,71,11,73,143,19,143,31,126,195,5,24,85,21,9,24,76,47,14,32,254,24,93,77,36,68,207,11,39,25,0,0,255,128,3,128,4,66,51,37,95,247,13,82,255,24,76,39,19,147,221,66,85,27,24,118,
6290
    7,8,24,74,249,12,76,74,8,91,234,8,67,80,17,131,222,33,253,0,121,30,44,73,0,16,69,15,6,32,0,65,23,38,69,231,12,65,179,6,98,131,16,86,31,27,24,108,157,14,80,160,8,24,65,46,17,33,4,0,
6291
    96,2,18,144,191,65,226,8,68,19,5,171,199,80,9,15,180,199,67,89,5,32,255,24,79,173,28,174,201,24,79,179,50,32,1,24,122,5,10,82,61,10,180,209,83,19,8,32,128,24,80,129,27,111,248,43,131,
6292
    71,24,115,103,8,67,127,41,78,213,24,100,247,19,66,115,39,75,107,5,32,254,165,219,78,170,40,24,112,163,49,32,1,97,203,6,65,173,64,32,0,83,54,7,133,217,88,37,12,32,254,131,28,33,128,
6293
    3,67,71,44,84,183,6,32,5,69,223,33,96,7,7,123,137,16,192,211,24,112,14,9,32,255,67,88,29,68,14,10,84,197,38,33,0,22,116,47,50,32,87,106,99,9,116,49,15,89,225,15,97,231,23,70,41,19,
6294
    82,85,8,93,167,6,32,253,132,236,108,190,7,89,251,5,116,49,58,33,128,128,131,234,32,15,24,74,67,38,70,227,24,24,83,45,23,89,219,12,70,187,12,89,216,19,32,2,69,185,24,141,24,70,143,66,
6295
    24,82,119,56,78,24,10,32,253,133,149,132,6,24,106,233,7,69,198,48,178,203,81,243,12,68,211,15,106,255,23,66,91,15,69,193,7,100,39,10,24,83,72,16,176,204,33,19,0,88,207,45,68,21,12,
6296
    68,17,10,65,157,53,68,17,6,32,254,92,67,10,65,161,25,69,182,43,24,118,91,47,69,183,18,181,209,111,253,12,89,159,8,66,112,12,69,184,45,35,0,0,0,9,24,80,227,26,73,185,16,118,195,15,131,
6297
    15,33,1,0,65,59,15,66,39,27,160,111,66,205,12,148,111,143,110,33,128,128,156,112,24,81,199,8,75,199,23,66,117,20,155,121,32,254,68,126,12,72,213,29,134,239,149,123,89,27,16,148,117,
6298
    65,245,8,24,71,159,14,141,134,134,28,73,51,55,109,77,15,105,131,11,68,67,11,76,169,27,107,209,12,102,174,8,32,128,72,100,18,116,163,56,79,203,11,75,183,44,85,119,19,71,119,23,151,227,
6299
    32,1,93,27,8,65,122,5,77,102,8,110,120,20,66,23,8,66,175,17,66,63,12,133,12,79,35,8,74,235,33,67,149,16,69,243,15,78,57,15,69,235,16,67,177,7,151,192,130,23,67,84,29,141,192,174,187,
6300
    77,67,15,69,11,12,159,187,77,59,10,199,189,24,70,235,50,96,83,19,66,53,23,105,65,19,77,47,12,163,199,66,67,37,78,207,50,67,23,23,174,205,67,228,6,71,107,13,67,22,14,66,85,11,83,187,
6301
    38,124,47,49,95,7,19,66,83,23,67,23,19,24,96,78,17,80,101,16,71,98,40,33,0,7,88,131,22,24,89,245,12,84,45,12,102,213,5,123,12,9,32,2,126,21,14,43,255,0,128,128,0,0,20,0,128,255,128,
6302
    3,126,19,39,32,75,106,51,7,113,129,15,24,110,135,19,126,47,15,115,117,11,69,47,11,32,2,109,76,9,102,109,9,32,128,75,2,10,130,21,32,254,69,47,6,32,3,94,217,47,32,0,65,247,10,69,15,46,
6303
    65,235,31,65,243,15,101,139,10,66,174,14,65,247,16,72,102,28,69,17,14,84,243,9,165,191,88,47,48,66,53,12,32,128,71,108,6,203,193,32,17,75,187,42,73,65,16,65,133,52,114,123,9,167,199,
6304
    69,21,37,86,127,44,75,171,11,180,197,78,213,12,148,200,81,97,46,24,95,243,9,32,4,66,75,33,113,103,9,87,243,36,143,225,24,84,27,31,90,145,8,148,216,67,49,5,24,84,34,14,75,155,27,67,
6305
    52,13,140,13,36,0,20,0,128,255,24,135,99,46,88,59,43,155,249,80,165,7,136,144,71,161,23,32,253,132,33,32,254,88,87,44,136,84,35,128,0,0,21,81,103,5,94,47,44,76,51,12,143,197,151,15,
6306
    65,215,31,24,64,77,13,65,220,20,65,214,14,71,4,40,65,213,13,32,0,130,0,35,21,1,2,0,135,0,34,36,0,72,134,10,36,1,0,26,0,130,134,11,36,2,0,14,0,108,134,11,32,3,138,23,32,4,138,11,34,
6307
    5,0,20,134,33,34,0,0,6,132,23,32,1,134,15,32,18,130,25,133,11,37,1,0,13,0,49,0,133,11,36,2,0,7,0,38,134,11,36,3,0,17,0,45,134,11,32,4,138,35,36,5,0,10,0,62,134,23,32,6,132,23,36,3,
6308
    0,1,4,9,130,87,131,167,133,11,133,167,133,11,133,167,133,11,37,3,0,34,0,122,0,133,11,133,167,133,11,133,167,133,11,133,167,34,50,0,48,130,1,34,52,0,47,134,5,8,49,49,0,53,98,121,32,
6309
    84,114,105,115,116,97,110,32,71,114,105,109,109,101,114,82,101,103,117,108,97,114,84,84,88,32,80,114,111,103,103,121,67,108,101,97,110,84,84,50,48,48,52,47,130,2,53,49,53,0,98,0,121,
6310
    0,32,0,84,0,114,0,105,0,115,0,116,0,97,0,110,130,15,32,71,132,15,36,109,0,109,0,101,130,9,32,82,130,5,36,103,0,117,0,108,130,29,32,114,130,43,34,84,0,88,130,35,32,80,130,25,34,111,
6311
    0,103,130,1,34,121,0,67,130,27,32,101,132,59,32,84,130,31,33,0,0,65,155,9,34,20,0,0,65,11,6,130,8,135,2,33,1,1,130,9,8,120,1,1,2,1,3,1,4,1,5,1,6,1,7,1,8,1,9,1,10,1,11,1,12,1,13,1,14,
6312
    1,15,1,16,1,17,1,18,1,19,1,20,1,21,1,22,1,23,1,24,1,25,1,26,1,27,1,28,1,29,1,30,1,31,1,32,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20,0,21,0,
6313
    22,0,23,0,24,0,25,0,26,0,27,0,28,0,29,0,30,0,31,130,187,8,66,33,0,34,0,35,0,36,0,37,0,38,0,39,0,40,0,41,0,42,0,43,0,44,0,45,0,46,0,47,0,48,0,49,0,50,0,51,0,52,0,53,0,54,0,55,0,56,0,
6314
    57,0,58,0,59,0,60,0,61,0,62,0,63,0,64,0,65,0,66,130,243,9,75,68,0,69,0,70,0,71,0,72,0,73,0,74,0,75,0,76,0,77,0,78,0,79,0,80,0,81,0,82,0,83,0,84,0,85,0,86,0,87,0,88,0,89,0,90,0,91,0,
6315
    92,0,93,0,94,0,95,0,96,0,97,1,33,1,34,1,35,1,36,1,37,1,38,1,39,1,40,1,41,1,42,1,43,1,44,1,45,1,46,1,47,1,48,1,49,1,50,1,51,1,52,1,53,1,54,1,55,1,56,1,57,1,58,1,59,1,60,1,61,1,62,1,
6316
    63,1,64,1,65,0,172,0,163,0,132,0,133,0,189,0,150,0,232,0,134,0,142,0,139,0,157,0,169,0,164,0,239,0,138,0,218,0,131,0,147,0,242,0,243,0,141,0,151,0,136,0,195,0,222,0,241,0,158,0,170,
6317
    0,245,0,244,0,246,0,162,0,173,0,201,0,199,0,174,0,98,0,99,0,144,0,100,0,203,0,101,0,200,0,202,0,207,0,204,0,205,0,206,0,233,0,102,0,211,0,208,0,209,0,175,0,103,0,240,0,145,0,214,0,
6318
    212,0,213,0,104,0,235,0,237,0,137,0,106,0,105,0,107,0,109,0,108,0,110,0,160,0,111,0,113,0,112,0,114,0,115,0,117,0,116,0,118,0,119,0,234,0,120,0,122,0,121,0,123,0,125,0,124,0,184,0,
6319
    161,0,127,0,126,0,128,0,129,0,236,0,238,0,186,14,117,110,105,99,111,100,101,35,48,120,48,48,48,49,141,14,32,50,141,14,32,51,141,14,32,52,141,14,32,53,141,14,32,54,141,14,32,55,141,
6320
    14,32,56,141,14,32,57,141,14,32,97,141,14,32,98,141,14,32,99,141,14,32,100,141,14,32,101,141,14,32,102,140,14,33,49,48,141,14,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,
6321
    141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,32,49,141,239,45,49,102,6,100,101,108,101,116,101,4,69,117,114,
6322
    111,140,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,32,56,141,236,
6323
    32,56,141,236,32,56,141,236,32,56,65,220,13,32,57,65,220,13,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,
6324
    239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247,
6325
};
6326
6327
static const char* GetDefaultCompressedFontDataTTF(int* out_size)
6328
1
{
6329
1
    *out_size = proggy_clean_ttf_compressed_size;
6330
1
    return (const char*)proggy_clean_ttf_compressed_data;
6331
1
}
6332
#endif // #ifndef IMGUI_DISABLE_DEFAULT_FONT
6333
6334
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui_internal.h
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (internal structures/api)
3
4
// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.
5
6
/*
7
8
Index of this file:
9
10
// [SECTION] Header mess
11
// [SECTION] Forward declarations
12
// [SECTION] Context pointer
13
// [SECTION] STB libraries includes
14
// [SECTION] Macros
15
// [SECTION] Generic helpers
16
// [SECTION] ImDrawList support
17
// [SECTION] Style support
18
// [SECTION] Data types support
19
// [SECTION] Widgets support: flags, enums, data structures
20
// [SECTION] Popup support
21
// [SECTION] Inputs support
22
// [SECTION] Clipper support
23
// [SECTION] Navigation support
24
// [SECTION] Typing-select support
25
// [SECTION] Columns support
26
// [SECTION] Box-select support
27
// [SECTION] Multi-select support
28
// [SECTION] Docking support
29
// [SECTION] Viewport support
30
// [SECTION] Settings support
31
// [SECTION] Localization support
32
// [SECTION] Error handling, State recovery support
33
// [SECTION] Metrics, Debug tools
34
// [SECTION] Generic context hooks
35
// [SECTION] ImGuiContext (main imgui context)
36
// [SECTION] ImGuiWindowTempData, ImGuiWindow
37
// [SECTION] Tab bar, Tab item support
38
// [SECTION] Table support
39
// [SECTION] ImGui internal API
40
// [SECTION] ImFontLoader
41
// [SECTION] ImFontAtlas internal API
42
// [SECTION] Test Engine specific hooks (imgui_test_engine)
43
44
*/
45
46
#pragma once
47
#ifndef IMGUI_DISABLE
48
49
//-----------------------------------------------------------------------------
50
// [SECTION] Header mess
51
//-----------------------------------------------------------------------------
52
53
#ifndef IMGUI_VERSION
54
#include "imgui.h"
55
#endif
56
57
#include <stdio.h>      // FILE*, sscanf
58
#include <stdlib.h>     // NULL, malloc, free, qsort, atoi, atof
59
#include <math.h>       // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf
60
#include <limits.h>     // INT_MIN, INT_MAX
61
62
// Enable SSE intrinsics if available
63
#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE)
64
#define IMGUI_ENABLE_SSE
65
#include <immintrin.h>
66
#if (defined __AVX__ || defined __SSE4_2__)
67
#define IMGUI_ENABLE_SSE4_2
68
#include <nmmintrin.h>
69
#endif
70
#endif
71
// Emscripten has partial SSE 4.2 support where _mm_crc32_u32 is not available. See https://emscripten.org/docs/porting/simd.html#id11 and #8213
72
#if defined(IMGUI_ENABLE_SSE4_2) && !defined(IMGUI_USE_LEGACY_CRC32_ADLER) && !defined(__EMSCRIPTEN__)
73
#define IMGUI_ENABLE_SSE4_2_CRC
74
#endif
75
76
// Visual Studio warnings
77
#ifdef _MSC_VER
78
#pragma warning (push)
79
#pragma warning (disable: 4251)     // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)
80
#pragma warning (disable: 26495)    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
81
#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
82
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
83
#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types
84
#endif
85
#endif
86
87
// Clang/GCC warnings with -Weverything
88
#if defined(__clang__)
89
#pragma clang diagnostic push
90
#if __has_warning("-Wunknown-warning-option")
91
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'
92
#endif
93
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
94
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor()
95
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast
96
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant
97
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function
98
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
99
#pragma clang diagnostic ignored "-Wmissing-noreturn"               // warning: function 'xxx' could be declared with attribute 'noreturn'
100
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
101
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
102
#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
103
#elif defined(__GNUC__)
104
#pragma GCC diagnostic push
105
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
106
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
107
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
108
#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
109
#endif
110
111
// In 1.89.4, we moved the implementation of "courtesy maths operators" from imgui_internal.h in imgui.h
112
// As they are frequently requested, we do not want to encourage to many people using imgui_internal.h
113
#if defined(IMGUI_DEFINE_MATH_OPERATORS) && !defined(IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED)
114
#error Please '#define IMGUI_DEFINE_MATH_OPERATORS' _BEFORE_ including imgui.h!
115
#endif
116
117
// Legacy defines
118
#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS            // Renamed in 1.74
119
#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
120
#endif
121
#ifdef IMGUI_DISABLE_MATH_FUNCTIONS                     // Renamed in 1.74
122
#error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
123
#endif
124
125
// Enable stb_truetype by default unless FreeType is enabled.
126
// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together.
127
#ifndef IMGUI_ENABLE_FREETYPE
128
#define IMGUI_ENABLE_STB_TRUETYPE
129
#endif
130
131
//-----------------------------------------------------------------------------
132
// [SECTION] Forward declarations
133
//-----------------------------------------------------------------------------
134
135
// Utilities
136
// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImStableVector<>, ImPool<>, ImChunkStream<>)
137
struct ImBitVector;                 // Store 1-bit per value
138
struct ImRect;                      // An axis-aligned rectangle (2 points)
139
struct ImGuiTextIndex;              // Maintain a line index for a text buffer.
140
141
// ImDrawList/ImFontAtlas
142
struct ImDrawDataBuilder;           // Helper to build a ImDrawData instance
143
struct ImDrawListSharedData;        // Data shared between all ImDrawList instances
144
struct ImFontAtlasBuilder;          // Internal storage for incrementally packing and building a ImFontAtlas
145
struct ImFontAtlasPostProcessData;  // Data available to potential texture post-processing functions
146
struct ImFontAtlasRectEntry;        // Packed rectangle lookup entry
147
148
// ImGui
149
struct ImGuiBoxSelectState;         // Box-selection state (currently used by multi-selection, could potentially be used by others)
150
struct ImGuiColorMod;               // Stacked color modifier, backup of modified data so we can restore it
151
struct ImGuiContext;                // Main Dear ImGui context
152
struct ImGuiContextHook;            // Hook for extensions like ImGuiTestEngine
153
struct ImGuiDataTypeInfo;           // Type information associated to a ImGuiDataType enum
154
struct ImGuiDeactivatedItemData;    // Data for IsItemDeactivated()/IsItemDeactivatedAfterEdit() function.
155
struct ImGuiErrorRecoveryState;     // Storage of stack sizes for error handling and recovery
156
struct ImGuiGroupData;              // Stacked storage data for BeginGroup()/EndGroup()
157
struct ImGuiInputTextState;         // Internal state of the currently focused/edited text input box
158
struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id
159
struct ImGuiLastItemData;           // Status storage for last submitted items
160
struct ImGuiLocEntry;               // A localization entry.
161
struct ImGuiMenuColumns;            // Simple column measurement, currently used for MenuItem() only
162
struct ImGuiMultiSelectState;       // Multi-selection persistent state (for focused selection).
163
struct ImGuiMultiSelectTempData;    // Multi-selection temporary state (while traversing).
164
struct ImGuiNavItemData;            // Result of a keyboard/gamepad directional navigation move query result
165
struct ImGuiMetricsConfig;          // Storage for ShowMetricsWindow() and DebugNodeXXX() functions
166
struct ImGuiNextWindowData;         // Storage for SetNextWindow** functions
167
struct ImGuiNextItemData;           // Storage for SetNextItem** functions
168
struct ImGuiOldColumnData;          // Storage data for a single column for legacy Columns() api
169
struct ImGuiOldColumns;             // Storage data for a columns set for legacy Columns() api
170
struct ImGuiPopupData;              // Storage for current popup stack
171
struct ImGuiSettingsHandler;        // Storage for one type registered in the .ini file
172
struct ImGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it
173
struct ImGuiStyleVarInfo;           // Style variable information (e.g. to access style variables from an enum)
174
struct ImGuiTabBar;                 // Storage for a tab bar
175
struct ImGuiTabItem;                // Storage for a tab item (within a tab bar)
176
struct ImGuiTable;                  // Storage for a table
177
struct ImGuiTableHeaderData;        // Storage for TableAngledHeadersRow()
178
struct ImGuiTableColumn;            // Storage for one column of a table
179
struct ImGuiTableInstanceData;      // Storage for one instance of a same table
180
struct ImGuiTableTempData;          // Temporary storage for one table (one per table in the stack), shared between tables.
181
struct ImGuiTableSettings;          // Storage for a table .ini settings
182
struct ImGuiTableColumnsSettings;   // Storage for a column .ini settings
183
struct ImGuiTreeNodeStackData;      // Temporary storage for TreeNode().
184
struct ImGuiTypingSelectState;      // Storage for GetTypingSelectRequest()
185
struct ImGuiTypingSelectRequest;    // Storage for GetTypingSelectRequest() (aimed to be public)
186
struct ImGuiWindow;                 // Storage for one window
187
struct ImGuiWindowTempData;         // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window)
188
struct ImGuiWindowSettings;         // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)
189
190
// Enumerations
191
// Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists.
192
enum ImGuiLocKey : int;                 // -> enum ImGuiLocKey              // Enum: a localization entry for translation.
193
typedef int ImGuiLayoutType;            // -> enum ImGuiLayoutType_         // Enum: Horizontal or vertical
194
195
// Flags
196
typedef int ImDrawTextFlags;            // -> enum ImDrawTextFlags_         // Flags: for ImTextCalcWordWrapPositionEx()
197
typedef int ImGuiActivateFlags;         // -> enum ImGuiActivateFlags_      // Flags: for navigation/focus function (will be for ActivateItem() later)
198
typedef int ImGuiDebugLogFlags;         // -> enum ImGuiDebugLogFlags_      // Flags: for ShowDebugLogWindow(), g.DebugLogFlags
199
typedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow()
200
typedef int ImGuiItemStatusFlags;       // -> enum ImGuiItemStatusFlags_    // Flags: for g.LastItemData.StatusFlags
201
typedef int ImGuiOldColumnFlags;        // -> enum ImGuiOldColumnFlags_     // Flags: for BeginColumns()
202
typedef int ImGuiLogFlags;              // -> enum ImGuiLogFlags_           // Flags: for LogBegin() text capturing function
203
typedef int ImGuiNavRenderCursorFlags;  // -> enum ImGuiNavRenderCursorFlags_//Flags: for RenderNavCursor()
204
typedef int ImGuiNavMoveFlags;          // -> enum ImGuiNavMoveFlags_       // Flags: for navigation requests
205
typedef int ImGuiNextItemDataFlags;     // -> enum ImGuiNextItemDataFlags_  // Flags: for SetNextItemXXX() functions
206
typedef int ImGuiNextWindowDataFlags;   // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions
207
typedef int ImGuiScrollFlags;           // -> enum ImGuiScrollFlags_        // Flags: for ScrollToItem() and navigation requests
208
typedef int ImGuiSeparatorFlags;        // -> enum ImGuiSeparatorFlags_     // Flags: for SeparatorEx()
209
typedef int ImGuiTextFlags;             // -> enum ImGuiTextFlags_          // Flags: for TextEx()
210
typedef int ImGuiTooltipFlags;          // -> enum ImGuiTooltipFlags_       // Flags: for BeginTooltipEx()
211
typedef int ImGuiTypingSelectFlags;     // -> enum ImGuiTypingSelectFlags_  // Flags: for GetTypingSelectRequest()
212
typedef int ImGuiWindowRefreshFlags;    // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy()
213
214
// Table column indexing
215
typedef ImS16 ImGuiTableColumnIdx;
216
typedef ImU16 ImGuiTableDrawChannelIdx;
217
218
//-----------------------------------------------------------------------------
219
// [SECTION] Context pointer
220
// See implementation of this variable in imgui.cpp for comments and details.
221
//-----------------------------------------------------------------------------
222
223
#ifndef GImGui
224
extern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer
225
#endif
226
227
//-----------------------------------------------------------------------------
228
// [SECTION] Macros
229
//-----------------------------------------------------------------------------
230
231
// Debug Printing Into TTY
232
// (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename)
233
#ifndef IMGUI_DEBUG_PRINTF
234
#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS
235
0
#define IMGUI_DEBUG_PRINTF(_FMT,...)    printf(_FMT, __VA_ARGS__)
236
#else
237
#define IMGUI_DEBUG_PRINTF(_FMT,...)    ((void)0)
238
#endif
239
#endif
240
241
// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.
242
0
#define IMGUI_DEBUG_LOG_ERROR(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventError)       IMGUI_DEBUG_LOG(__VA_ARGS__); else g.DebugLogSkippedErrors++; } while (0)
243
0
#define IMGUI_DEBUG_LOG_ACTIVEID(...)   do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
244
9
#define IMGUI_DEBUG_LOG_FOCUS(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
245
4
#define IMGUI_DEBUG_LOG_POPUP(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
246
4
#define IMGUI_DEBUG_LOG_NAV(...)        do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav)         IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
247
0
#define IMGUI_DEBUG_LOG_SELECTION(...)  do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)   IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
248
0
#define IMGUI_DEBUG_LOG_CLIPPER(...)    do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper)     IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
249
0
#define IMGUI_DEBUG_LOG_IO(...)         do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)          IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
250
4
#define IMGUI_DEBUG_LOG_FONT(...)       do { ImGuiContext* g2 = GImGui; if (g2 && g2->DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Called from ImFontAtlas function which may operate without a context.
251
161k
#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)
252
253
// Static Asserts
254
1.29M
#define IM_STATIC_ASSERT(_COND)         static_assert(_COND, "")
255
256
// "Paranoid" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much.
257
// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code.
258
//#define IMGUI_DEBUG_PARANOID
259
#ifdef IMGUI_DEBUG_PARANOID
260
#define IM_ASSERT_PARANOID(_EXPR)       IM_ASSERT(_EXPR)
261
#else
262
#define IM_ASSERT_PARANOID(_EXPR)
263
#endif
264
265
// Misc Macros
266
49
#define IM_PI                           3.14159265358979323846f
267
#ifdef _WIN32
268
#define IM_NEWLINE                      "\r\n"   // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!)
269
#else
270
0
#define IM_NEWLINE                      "\n"
271
#endif
272
#ifndef IM_TABSIZE                      // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override
273
0
#define IM_TABSIZE                      (4)
274
#endif
275
30
#define IM_MEMALIGN(_OFF,_ALIGN)        (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))           // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8
276
0
#define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose
277
3.89M
#define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255
278
2.99M
#define IM_TRUNC(_VAL)                  ((float)(int)(_VAL))                                    // ImTrunc() is not inlined in MSVC debug builds
279
1.61M
#define IM_ROUND(_VAL)                  ((float)(int)((_VAL) + 0.5f))                           //
280
#define IM_STRINGIFY_HELPER(_X)         #_X
281
#define IM_STRINGIFY(_X)                IM_STRINGIFY_HELPER(_X)                                 // Preprocessor idiom to stringify e.g. an integer.
282
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
283
#define IM_FLOOR IM_TRUNC
284
#endif
285
286
// Hint for branch prediction
287
#if (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 202002L))
288
1.54M
#define IM_LIKELY   [[likely]]
289
#define IM_UNLIKELY [[unlikely]]
290
#else
291
#define IM_LIKELY
292
#define IM_UNLIKELY
293
#endif
294
295
// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall
296
#ifdef _MSC_VER
297
#define IMGUI_CDECL __cdecl
298
#else
299
#define IMGUI_CDECL
300
#endif
301
302
// Warnings
303
#if defined(_MSC_VER) && !defined(__clang__)
304
#define IM_MSVC_WARNING_SUPPRESS(XXXX)  __pragma(warning(suppress: XXXX))
305
#else
306
#define IM_MSVC_WARNING_SUPPRESS(XXXX)
307
#endif
308
309
// Debug Tools
310
// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item.
311
// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference.
312
#ifndef IM_DEBUG_BREAK
313
#if defined (_MSC_VER)
314
#define IM_DEBUG_BREAK()    __debugbreak()
315
#elif defined(__clang__)
316
0
#define IM_DEBUG_BREAK()    __builtin_debugtrap()
317
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
318
#define IM_DEBUG_BREAK()    __asm__ volatile("int3;nop")
319
#elif defined(__GNUC__) && defined(__thumb__)
320
#define IM_DEBUG_BREAK()    __asm__ volatile(".inst 0xde01")
321
#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)
322
#define IM_DEBUG_BREAK()    __asm__ volatile(".inst 0xe7f001f0")
323
#else
324
#define IM_DEBUG_BREAK()    IM_ASSERT(0)    // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!
325
#endif
326
#endif // #ifndef IM_DEBUG_BREAK
327
328
// Format specifiers, printing 64-bit hasn't been decently standardized...
329
// In a real application you should be using PRId64 and PRIu64 from <inttypes.h> (non-windows) and on Windows define them yourself.
330
#if defined(_MSC_VER) && !defined(__clang__)
331
#define IM_PRId64   "I64d"
332
#define IM_PRIu64   "I64u"
333
#define IM_PRIX64   "I64X"
334
#else
335
#define IM_PRId64   "lld"
336
#define IM_PRIu64   "llu"
337
#define IM_PRIX64   "llX"
338
#endif
339
#define IM_TEXTUREID_TO_U64(_TEXID) ((ImU64)(intptr_t)(_TEXID))
340
341
//-----------------------------------------------------------------------------
342
// [SECTION] Generic helpers
343
// Note that the ImXXX helpers functions are lower-level than ImGui functions.
344
// ImGui functions or the ImGui context are never called/used from other ImXXX functions.
345
//-----------------------------------------------------------------------------
346
// - Helpers: Hashing
347
// - Helpers: Sorting
348
// - Helpers: Bit manipulation
349
// - Helpers: String
350
// - Helpers: Formatting
351
// - Helpers: UTF-8 <> wchar conversions
352
// - Helpers: ImVec2/ImVec4 operators
353
// - Helpers: Maths
354
// - Helpers: Geometry
355
// - Helper: ImVec1
356
// - Helper: ImVec2ih
357
// - Helper: ImRect
358
// - Helper: ImBitArray
359
// - Helper: ImBitVector
360
// - Helper: ImSpan<>, ImSpanAllocator<>
361
// - Helper: ImStableVector<>
362
// - Helper: ImPool<>
363
// - Helper: ImChunkStream<>
364
// - Helper: ImGuiTextIndex
365
// - Helper: ImGuiStorage
366
//-----------------------------------------------------------------------------
367
368
// Helpers: Hashing
369
IMGUI_API ImGuiID       ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);
370
IMGUI_API ImGuiID       ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0);
371
IMGUI_API const char*   ImHashSkipUncontributingPrefix(const char* label);
372
373
// Helpers: Sorting
374
#ifndef ImQsort
375
81.2k
inline void             ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); }
376
#endif
377
378
// Helpers: Color Blending
379
IMGUI_API ImU32         ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
380
381
// Helpers: Bit manipulation
382
404k
inline bool             ImIsPowerOfTwo(int v)               { return v != 0 && (v & (v - 1)) == 0; }
383
0
inline bool             ImIsPowerOfTwo(ImU64 v)             { return v != 0 && (v & (v - 1)) == 0; }
384
2
inline int              ImUpperPowerOfTwo(int v)            { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
385
0
inline unsigned int     ImCountSetBits(unsigned int v)      { unsigned int count = 0; while (v > 0) { v = v & (v - 1); count++; } return count; }
386
387
// Helpers: String
388
64
#define ImStrlen strlen
389
58
#define ImMemchr memchr
390
IMGUI_API int           ImStricmp(const char* str1, const char* str2);                      // Case insensitive compare.
391
IMGUI_API int           ImStrnicmp(const char* str1, const char* str2, size_t count);       // Case insensitive compare to a certain count.
392
IMGUI_API void          ImStrncpy(char* dst, const char* src, size_t count);                // Copy to a certain count and always zero terminate (strncpy doesn't).
393
IMGUI_API char*         ImStrdup(const char* str);                                          // Duplicate a string.
394
IMGUI_API void*         ImMemdup(const void* src, size_t size);                             // Duplicate a chunk of memory.
395
IMGUI_API char*         ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);        // Copy in provided buffer, recreate buffer if needed.
396
IMGUI_API const char*   ImStrchrRange(const char* str_begin, const char* str_end, char c);  // Find first occurrence of 'c' in string range.
397
IMGUI_API const char*   ImStreolRange(const char* str, const char* str_end);                // End end-of-line
398
IMGUI_API const char*   ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);  // Find a substring in a string range.
399
IMGUI_API void          ImStrTrimBlanks(char* str);                                         // Remove leading and trailing blanks from a buffer.
400
IMGUI_API const char*   ImStrSkipBlank(const char* str);                                    // Find first non-blank character.
401
IMGUI_API int           ImStrlenW(const ImWchar* str);                                      // Computer string length (ImWchar string)
402
IMGUI_API const char*   ImStrbol(const char* buf_mid_line, const char* buf_begin);          // Find beginning-of-line
403
IM_MSVC_RUNTIME_CHECKS_OFF
404
0
inline char             ImToUpper(char c)               { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
405
0
inline bool             ImCharIsBlankA(char c)          { return c == ' ' || c == '\t'; }
406
0
inline bool             ImCharIsBlankW(unsigned int c)  { return c == ' ' || c == '\t' || c == 0x3000; }
407
0
inline bool             ImCharIsXdigitA(char c)         { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }
408
IM_MSVC_RUNTIME_CHECKS_RESTORE
409
410
// Helpers: Formatting
411
IMGUI_API int           ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);
412
IMGUI_API int           ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);
413
IMGUI_API void          ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3);
414
IMGUI_API void          ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3);
415
IMGUI_API const char*   ImParseFormatFindStart(const char* format);
416
IMGUI_API const char*   ImParseFormatFindEnd(const char* format);
417
IMGUI_API const char*   ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);
418
IMGUI_API void          ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size);
419
IMGUI_API const char*   ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size);
420
IMGUI_API int           ImParseFormatPrecision(const char* format, int default_value);
421
422
// Helpers: UTF-8 <> wchar conversions
423
IMGUI_API int           ImTextCharToUtf8(char out_buf[5], unsigned int c);                                                      // return output UTF-8 bytes count
424
IMGUI_API int           ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end);   // return output UTF-8 bytes count
425
IMGUI_API int           ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);               // read one character. return input UTF-8 bytes count
426
IMGUI_API int           ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL);   // return input UTF-8 bytes count
427
IMGUI_API int           ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end);                                 // return number of UTF-8 code-points (NOT bytes count)
428
IMGUI_API int           ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end);                             // return number of bytes to express one char in UTF-8
429
IMGUI_API int           ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                        // return number of bytes to express string in UTF-8
430
IMGUI_API const char*   ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr);                   // return previous UTF-8 code-point.
431
IMGUI_API int           ImTextCountLines(const char* in_text, const char* in_text_end);                                         // return number of lines taken by text. trailing carriage return doesn't count as an extra line.
432
433
// Helpers: High-level text functions (DO NOT USE!!! THIS IS A MINIMAL SUBSET OF LARGER UPCOMING CHANGES)
434
enum ImDrawTextFlags_
435
{
436
    ImDrawTextFlags_None                = 0,
437
    ImDrawTextFlags_CpuFineClip         = 1 << 0,    // Must be == 1/true for legacy with 'bool cpu_fine_clip' arg to RenderText()
438
    ImDrawTextFlags_WrapKeepBlanks      = 1 << 1,
439
    ImDrawTextFlags_StopOnNewLine       = 1 << 2,
440
};
441
IMGUI_API ImVec2        ImFontCalcTextSizeEx(ImFont* font, float size, float max_width, float wrap_width, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags);
442
IMGUI_API const char*   ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* text, const char* text_end, float wrap_width, ImDrawTextFlags flags = 0);
443
IMGUI_API const char*   ImTextCalcWordWrapNextLineStart(const char* text, const char* text_end, ImDrawTextFlags flags = 0); // trim trailing space and find beginning of next line
444
445
// Helpers: File System
446
#ifdef IMGUI_DISABLE_FILE_FUNCTIONS
447
#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
448
typedef void* ImFileHandle;
449
inline ImFileHandle         ImFileOpen(const char*, const char*)                    { return NULL; }
450
inline bool                 ImFileClose(ImFileHandle)                               { return false; }
451
inline ImU64                ImFileGetSize(ImFileHandle)                             { return (ImU64)-1; }
452
inline ImU64                ImFileRead(void*, ImU64, ImU64, ImFileHandle)           { return 0; }
453
inline ImU64                ImFileWrite(const void*, ImU64, ImU64, ImFileHandle)    { return 0; }
454
#endif
455
#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS
456
typedef FILE* ImFileHandle;
457
IMGUI_API ImFileHandle      ImFileOpen(const char* filename, const char* mode);
458
IMGUI_API bool              ImFileClose(ImFileHandle file);
459
IMGUI_API ImU64             ImFileGetSize(ImFileHandle file);
460
IMGUI_API ImU64             ImFileRead(void* data, ImU64 size, ImU64 count, ImFileHandle file);
461
IMGUI_API ImU64             ImFileWrite(const void* data, ImU64 size, ImU64 count, ImFileHandle file);
462
#else
463
#define IMGUI_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions
464
#endif
465
IMGUI_API void*             ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0);
466
467
// Helpers: Maths
468
IM_MSVC_RUNTIME_CHECKS_OFF
469
// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy)
470
#ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS
471
763
#define ImFabs(X)           fabsf(X)
472
0
#define ImSqrt(X)           sqrtf(X)
473
0
#define ImFmod(X, Y)        fmodf((X), (Y))
474
50
#define ImCos(X)            cosf(X)
475
48
#define ImSin(X)            sinf(X)
476
0
#define ImAcos(X)           acosf(X)
477
0
#define ImAtan2(Y, X)       atan2f((Y), (X))
478
0
#define ImAtof(STR)         atof(STR)
479
81
#define ImCeil(X)           ceilf(X)
480
0
inline float  ImPow(float x, float y)    { return powf(x, y); }          // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision
481
0
inline double ImPow(double x, double y)  { return pow(x, y); }
482
0
inline float  ImLog(float x)             { return logf(x); }             // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision
483
0
inline double ImLog(double x)            { return log(x); }
484
0
inline int    ImAbs(int x)               { return x < 0 ? -x : x; }
485
2.38k
inline float  ImAbs(float x)             { return fabsf(x); }
486
0
inline double ImAbs(double x)            { return fabs(x); }
487
0
inline float  ImSign(float x)            { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument
488
0
inline double ImSign(double x)           { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; }
489
#ifdef IMGUI_ENABLE_SSE
490
inline float  ImRsqrt(float x)           { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }
491
#else
492
890k
inline float  ImRsqrt(float x)           { return 1.0f / sqrtf(x); }
493
#endif
494
0
inline double ImRsqrt(double x)          { return 1.0 / sqrt(x); }
495
#endif
496
// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double
497
// (Exceptionally using templates here but we could also redefine them for those types)
498
728k
template<typename T> T ImMin(T lhs, T rhs)                              { return lhs < rhs ? lhs : rhs; }
Unexecuted instantiation: _Z5ImMinImET_S0_S0_
_Z5ImMinIfET_S0_S0_
Line
Count
Source
498
648k
template<typename T> T ImMin(T lhs, T rhs)                              { return lhs < rhs ? lhs : rhs; }
_Z5ImMinIiET_S0_S0_
Line
Count
Source
498
80.5k
template<typename T> T ImMin(T lhs, T rhs)                              { return lhs < rhs ? lhs : rhs; }
Unexecuted instantiation: _Z5ImMinIjET_S0_S0_
_Z5ImMinItET_S0_S0_
Line
Count
Source
498
88
template<typename T> T ImMin(T lhs, T rhs)                              { return lhs < rhs ? lhs : rhs; }
Unexecuted instantiation: _Z5ImMinIxET_S0_S0_
Unexecuted instantiation: _Z5ImMinIyET_S0_S0_
Unexecuted instantiation: _Z5ImMinIdET_S0_S0_
499
6.48M
template<typename T> T ImMax(T lhs, T rhs)                              { return lhs >= rhs ? lhs : rhs; }
_Z5ImMaxIfET_S0_S0_
Line
Count
Source
499
6.47M
template<typename T> T ImMax(T lhs, T rhs)                              { return lhs >= rhs ? lhs : rhs; }
_Z5ImMaxIiET_S0_S0_
Line
Count
Source
499
176
template<typename T> T ImMax(T lhs, T rhs)                              { return lhs >= rhs ? lhs : rhs; }
Unexecuted instantiation: _Z5ImMaxIsET_S0_S0_
Unexecuted instantiation: _Z5ImMaxIxET_S0_S0_
_Z5ImMaxItET_S0_S0_
Line
Count
Source
499
7.92k
template<typename T> T ImMax(T lhs, T rhs)                              { return lhs >= rhs ? lhs : rhs; }
_Z5ImMaxIjET_S0_S0_
Line
Count
Source
499
1.98k
template<typename T> T ImMax(T lhs, T rhs)                              { return lhs >= rhs ? lhs : rhs; }
500
404k
template<typename T> T ImClamp(T v, T mn, T mx)                         { return (v < mn) ? mn : (v > mx) ? mx : v; }
_Z7ImClampIiET_S0_S0_S0_
Line
Count
Source
500
63
template<typename T> T ImClamp(T v, T mn, T mx)                         { return (v < mn) ? mn : (v > mx) ? mx : v; }
_Z7ImClampIfET_S0_S0_S0_
Line
Count
Source
500
404k
template<typename T> T ImClamp(T v, T mn, T mx)                         { return (v < mn) ? mn : (v > mx) ? mx : v; }
Unexecuted instantiation: _Z7ImClampIxET_S0_S0_S0_
Unexecuted instantiation: _Z7ImClampIjET_S0_S0_S0_
Unexecuted instantiation: _Z7ImClampIyET_S0_S0_S0_
Unexecuted instantiation: _Z7ImClampIdET_S0_S0_S0_
501
0
template<typename T> T ImLerp(T a, T b, float t)                        { return (T)(a + (b - a) * t); }
Unexecuted instantiation: _Z6ImLerpIiET_S0_S0_f
Unexecuted instantiation: _Z6ImLerpIfET_S0_S0_f
Unexecuted instantiation: _Z6ImLerpIjET_S0_S0_f
Unexecuted instantiation: _Z6ImLerpIxET_S0_S0_f
Unexecuted instantiation: _Z6ImLerpIyET_S0_S0_f
Unexecuted instantiation: _Z6ImLerpIdET_S0_S0_f
502
0
template<typename T> void ImSwap(T& a, T& b)                            { T tmp = a; a = b; b = tmp; }
Unexecuted instantiation: _Z6ImSwapI21ImGuiListClipperRangeEvRT_S2_
Unexecuted instantiation: _Z6ImSwapIfEvRT_S1_
Unexecuted instantiation: _Z6ImSwapIsEvRT_S1_
Unexecuted instantiation: _Z6ImSwapIjEvRT_S1_
Unexecuted instantiation: _Z6ImSwapIxEvRT_S1_
Unexecuted instantiation: _Z6ImSwapIdEvRT_S1_
Unexecuted instantiation: _Z6ImSwapIyEvRT_S1_
Unexecuted instantiation: _Z6ImSwapIPKvEvRT_S3_
Unexecuted instantiation: _Z6ImSwapIiEvRT_S1_
503
0
template<typename T> T ImAddClampOverflow(T a, T b, T mn, T mx)         { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }
Unexecuted instantiation: _Z18ImAddClampOverflowIaET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowIhET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowIsET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowItET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowIiET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowIjET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowIxET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImAddClampOverflowIyET_S0_S0_S0_S0_
504
0
template<typename T> T ImSubClampOverflow(T a, T b, T mn, T mx)         { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }
Unexecuted instantiation: _Z18ImSubClampOverflowIaET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowIhET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowIsET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowItET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowIiET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowIjET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowIxET_S0_S0_S0_S0_
Unexecuted instantiation: _Z18ImSubClampOverflowIyET_S0_S0_S0_S0_
505
// - Misc maths helpers
506
812k
inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs)               { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }
507
1.45M
inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs)               { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }
508
566k
inline ImVec2 ImClamp(const ImVec2& v, const ImVec2&mn, const ImVec2&mx){ return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }
509
0
inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t)         { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }
510
0
inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }
511
8
inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t)         { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }
512
3.89M
inline float  ImSaturate(float f)                                       { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }
513
161k
inline float  ImLengthSqr(const ImVec2& lhs)                            { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }
514
0
inline float  ImLengthSqr(const ImVec4& lhs)                            { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }
515
0
inline float  ImInvLength(const ImVec2& lhs, float fail_value)          { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }
516
485k
inline float  ImTrunc(float f)                                          { return (float)(int)(f); }
517
243k
inline ImVec2 ImTrunc(const ImVec2& v)                                  { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }
518
812k
inline float  ImFloor(float f)                                          { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()
519
80.5k
inline ImVec2 ImFloor(const ImVec2& v)                                  { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }
520
647k
inline float  ImTrunc64(float f)                                        { return (float)(ImS64)(f); }
521
323k
inline float  ImRound64(float f)                                        { return (float)(ImS64)(f + 0.5f); }
522
0
inline int    ImModPositive(int a, int b)                               { return (a + b) % b; }
523
0
inline float  ImDot(const ImVec2& a, const ImVec2& b)                   { return a.x * b.x + a.y * b.y; }
524
0
inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a)       { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }
525
0
inline float  ImLinearSweep(float current, float target, float speed)   { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }
526
0
inline float  ImLinearRemapClamp(float s0, float s1, float d0, float d1, float x) { return ImSaturate((x - s0) / (s1 - s0)) * (d1 - d0) + d0; }
527
0
inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs)               { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }
528
0
inline bool   ImIsFloatAboveGuaranteedIntegerPrecision(float f)         { return f <= -16777216 || f >= 16777216; }
529
2.38k
inline float  ImExponentialMovingAverage(float avg, float sample, int n){ avg -= avg / n; avg += sample / n; return avg; }
530
IM_MSVC_RUNTIME_CHECKS_RESTORE
531
532
// Helpers: Geometry
533
IMGUI_API ImVec2     ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t);
534
IMGUI_API ImVec2     ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments);       // For curves with explicit number of segments
535
IMGUI_API ImVec2     ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol
536
IMGUI_API ImVec2     ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t);
537
IMGUI_API ImVec2     ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p);
538
IMGUI_API bool       ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);
539
IMGUI_API ImVec2     ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);
540
IMGUI_API void       ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w);
541
0
inline float         ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c)          { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; }
542
0
inline bool          ImTriangleIsClockwise(const ImVec2& a, const ImVec2& b, const ImVec2& c)   { return ((b.x - a.x) * (c.y - b.y)) - ((c.x - b.x) * (b.y - a.y)) > 0.0f; }
543
544
// Helper: ImVec1 (1D vector)
545
// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)
546
IM_MSVC_RUNTIME_CHECKS_OFF
547
struct ImVec1
548
{
549
    float   x;
550
9
    constexpr ImVec1()         : x(0.0f) { }
551
0
    constexpr ImVec1(float _x) : x(_x) { }
552
};
553
554
// Helper: ImVec2i (2D vector, integer)
555
struct ImVec2i
556
{
557
    int         x, y;
558
2
    constexpr ImVec2i()                             : x(0), y(0) {}
559
4
    constexpr ImVec2i(int _x, int _y)               : x(_x), y(_y) {}
560
};
561
562
// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)
563
struct ImVec2ih
564
{
565
    short   x, y;
566
12
    constexpr ImVec2ih()                           : x(0), y(0) {}
567
6
    constexpr ImVec2ih(short _x, short _y)         : x(_x), y(_y) {}
568
2
    constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {}
569
};
570
571
// Helper: ImRect (2D axis aligned bounding-box)
572
// NB: we can't rely on ImVec2 math operators being available here!
573
struct IMGUI_API ImRect
574
{
575
    ImVec2      Min;    // Upper-left
576
    ImVec2      Max;    // Lower-right
577
578
81.2k
    constexpr ImRect()                                        : Min(0.0f, 0.0f), Max(0.0f, 0.0f)  {}
579
1.05M
    constexpr ImRect(const ImVec2& min, const ImVec2& max)    : Min(min), Max(max)                {}
580
808k
    constexpr ImRect(const ImVec4& v)                         : Min(v.x, v.y), Max(v.z, v.w)      {}
581
1.61M
    constexpr ImRect(float x1, float y1, float x2, float y2)  : Min(x1, y1), Max(x2, y2)          {}
582
583
0
    ImVec2      GetCenter() const                   { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }
584
242k
    ImVec2      GetSize() const                     { return ImVec2(Max.x - Min.x, Max.y - Min.y); }
585
243k
    float       GetWidth() const                    { return Max.x - Min.x; }
586
243k
    float       GetHeight() const                   { return Max.y - Min.y; }
587
0
    float       GetArea() const                     { return (Max.x - Min.x) * (Max.y - Min.y); }
588
0
    ImVec2      GetTL() const                       { return Min; }                   // Top-left
589
0
    ImVec2      GetTR() const                       { return ImVec2(Max.x, Min.y); }  // Top-right
590
0
    ImVec2      GetBL() const                       { return ImVec2(Min.x, Max.y); }  // Bottom-left
591
0
    ImVec2      GetBR() const                       { return Max; }                   // Bottom-right
592
0
    bool        Contains(const ImVec2& p) const     { return p.x     >= Min.x && p.y     >= Min.y && p.x     <  Max.x && p.y     <  Max.y; }
593
0
    bool        Contains(const ImRect& r) const     { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }
594
570k
    bool        ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; }
595
324k
    bool        Overlaps(const ImRect& r) const     { return r.Min.y <  Max.y && r.Max.y >  Min.y && r.Min.x <  Max.x && r.Max.x >  Min.x; }
596
0
    void        Add(const ImVec2& p)                { if (Min.x > p.x)     Min.x = p.x;     if (Min.y > p.y)     Min.y = p.y;     if (Max.x < p.x)     Max.x = p.x;     if (Max.y < p.y)     Max.y = p.y; }
597
80.5k
    void        Add(const ImRect& r)                { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }
598
0
    void        Expand(const float amount)          { Min.x -= amount;   Min.y -= amount;   Max.x += amount;   Max.y += amount; }
599
660
    void        Expand(const ImVec2& amount)        { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }
600
0
    void        Translate(const ImVec2& d)          { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }
601
0
    void        TranslateX(float dx)                { Min.x += dx; Max.x += dx; }
602
662
    void        TranslateY(float dy)                { Min.y += dy; Max.y += dy; }
603
650k
    void        ClipWith(const ImRect& r)           { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); }                   // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.
604
161k
    void        ClipWithFull(const ImRect& r)       { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.
605
0
    void        Floor()                             { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); }
606
1.32k
    bool        IsInverted() const                  { return Min.x > Max.x || Min.y > Max.y; }
607
80.5k
    ImVec4      ToVec4() const                      { return ImVec4(Min.x, Min.y, Max.x, Max.y); }
608
0
    const ImVec4& AsVec4() const                    { return *(const ImVec4*)&Min.x; }
609
};
610
611
// Helper: ImBitArray
612
864
#define         IM_BITARRAY_TESTBIT(_ARRAY, _N)                 ((_ARRAY[(_N) >> 5] & ((ImU32)1 << ((_N) & 31))) != 0) // Macro version of ImBitArrayTestBit(): ensure args have side-effect or are costly!
613
0
#define         IM_BITARRAY_CLEARBIT(_ARRAY, _N)                ((_ARRAY[(_N) >> 5] &= ~((ImU32)1 << ((_N) & 31))))    // Macro version of ImBitArrayClearBit(): ensure args have side-effect or are costly!
614
0
inline size_t   ImBitArrayGetStorageSizeInBytes(int bitcount)   { return (size_t)((bitcount + 31) >> 5) << 2; }
615
0
inline void     ImBitArrayClearAllBits(ImU32* arr, int bitcount){ memset(arr, 0, ImBitArrayGetStorageSizeInBytes(bitcount)); }
616
0
inline bool     ImBitArrayTestBit(const ImU32* arr, int n)      { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }
617
0
inline void     ImBitArrayClearBit(ImU32* arr, int n)           { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; }
618
604
inline void     ImBitArraySetBit(ImU32* arr, int n)             { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; }
619
inline void     ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on range [n..n2)
620
0
{
621
0
    n2--;
622
0
    while (n <= n2)
623
0
    {
624
0
        int a_mod = (n & 31);
625
0
        int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1;
626
0
        ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1);
627
0
        arr[n >> 5] |= mask;
628
0
        n = (n + 32) & ~31;
629
0
    }
630
0
}
631
632
typedef ImU32* ImBitArrayPtr; // Name for use in structs
633
634
// Helper: ImBitArray class (wrapper over ImBitArray functions)
635
// Store 1-bit per value.
636
template<int BITCOUNT, int OFFSET = 0>
637
struct ImBitArray
638
{
639
    ImU32           Storage[(BITCOUNT + 31) >> 5];
640
80.5k
    ImBitArray()                                { ClearAllBits(); }
_ZN10ImBitArrayILi155ELin512EEC2Ev
Line
Count
Source
640
1
    ImBitArray()                                { ClearAllBits(); }
_ZN10ImBitArrayILi155ELi0EEC2Ev
Line
Count
Source
640
80.5k
    ImBitArray()                                { ClearAllBits(); }
641
80.5k
    void            ClearAllBits()              { memset(Storage, 0, sizeof(Storage)); }
_ZN10ImBitArrayILi155ELin512EE12ClearAllBitsEv
Line
Count
Source
641
1
    void            ClearAllBits()              { memset(Storage, 0, sizeof(Storage)); }
_ZN10ImBitArrayILi155ELi0EE12ClearAllBitsEv
Line
Count
Source
641
80.5k
    void            ClearAllBits()              { memset(Storage, 0, sizeof(Storage)); }
642
    void            SetAllBits()                { memset(Storage, 255, sizeof(Storage)); }
643
864
    bool            TestBit(int n) const        { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); }
_ZNK10ImBitArrayILi155ELin512EE7TestBitEi
Line
Count
Source
643
324
    bool            TestBit(int n) const        { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); }
_ZNK10ImBitArrayILi155ELi0EE7TestBitEi
Line
Count
Source
643
540
    bool            TestBit(int n) const        { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); }
644
604
    void            SetBit(int n)               { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); }
_ZN10ImBitArrayILi155ELin512EE6SetBitEi
Line
Count
Source
644
64
    void            SetBit(int n)               { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); }
_ZN10ImBitArrayILi155ELi0EE6SetBitEi
Line
Count
Source
644
540
    void            SetBit(int n)               { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); }
645
    void            ClearBit(int n)             { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); }
646
    void            SetBitRange(int n, int n2)  { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)
647
    bool            operator[](int n) const     { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); }
648
};
649
650
// Helper: ImBitVector
651
// Store 1-bit per value.
652
struct IMGUI_API ImBitVector
653
{
654
    ImVector<ImU32> Storage;
655
0
    void            Create(int sz)              { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }
656
0
    void            Clear()                     { Storage.clear(); }
657
0
    bool            TestBit(int n) const        { IM_ASSERT(n < (Storage.Size << 5)); return IM_BITARRAY_TESTBIT(Storage.Data, n); }
658
0
    void            SetBit(int n)               { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); }
659
0
    void            ClearBit(int n)             { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); }
660
};
661
IM_MSVC_RUNTIME_CHECKS_RESTORE
662
663
// Helper: ImSpan<>
664
// Pointing to a span of data we don't own.
665
template<typename T>
666
struct ImSpan
667
{
668
    T*                  Data;
669
    T*                  DataEnd;
670
671
    // Constructors, destructor
672
0
    inline ImSpan()                                 { Data = DataEnd = NULL; }
Unexecuted instantiation: _ZN6ImSpanI16ImGuiTableColumnEC2Ev
Unexecuted instantiation: _ZN6ImSpanIsEC2Ev
Unexecuted instantiation: _ZN6ImSpanI18ImGuiTableCellDataEC2Ev
673
    inline ImSpan(T* data, int size)                { Data = data; DataEnd = data + size; }
674
    inline ImSpan(T* data, T* data_end)             { Data = data; DataEnd = data_end; }
675
676
    inline void         set(T* data, int size)      { Data = data; DataEnd = data + size; }
677
0
    inline void         set(T* data, T* data_end)   { Data = data; DataEnd = data_end; }
Unexecuted instantiation: _ZN6ImSpanI16ImGuiTableColumnE3setEPS0_S2_
Unexecuted instantiation: _ZN6ImSpanIsE3setEPsS1_
Unexecuted instantiation: _ZN6ImSpanI18ImGuiTableCellDataE3setEPS0_S2_
678
0
    inline int          size() const                { return (int)(ptrdiff_t)(DataEnd - Data); }
679
    inline int          size_in_bytes() const       { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); }
680
0
    inline T&           operator[](int i)           { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }
Unexecuted instantiation: _ZN6ImSpanI16ImGuiTableColumnEixEi
Unexecuted instantiation: _ZN6ImSpanIsEixEi
Unexecuted instantiation: _ZN6ImSpanI18ImGuiTableCellDataEixEi
681
0
    inline const T&     operator[](int i) const     { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }
682
683
    inline T*           begin()                     { return Data; }
684
    inline const T*     begin() const               { return Data; }
685
    inline T*           end()                       { return DataEnd; }
686
    inline const T*     end() const                 { return DataEnd; }
687
688
    // Utilities
689
0
    inline int  index_from_ptr(const T* it) const   { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; }
690
};
691
692
// Helper: ImSpanAllocator<>
693
// Facilitate storing multiple chunks into a single large block (the "arena")
694
// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges.
695
template<int CHUNKS>
696
struct ImSpanAllocator
697
{
698
    char*   BasePtr;
699
    int     CurrOff;
700
    int     CurrIdx;
701
    int     Offsets[CHUNKS];
702
    int     Sizes[CHUNKS];
703
704
0
    ImSpanAllocator()                               { memset(this, 0, sizeof(*this)); }
705
0
    inline void  Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; }
706
0
    inline int   GetArenaSizeInBytes()              { return CurrOff; }
707
0
    inline void  SetArenaBasePtr(void* base_ptr)    { BasePtr = (char*)base_ptr; }
708
0
    inline void* GetSpanPtrBegin(int n)             { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); }
709
0
    inline void* GetSpanPtrEnd(int n)               { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); }
710
    template<typename T>
711
0
    inline void  GetSpan(int n, ImSpan<T>* span)    { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }
Unexecuted instantiation: _ZN15ImSpanAllocatorILi6EE7GetSpanI16ImGuiTableColumnEEviP6ImSpanIT_E
Unexecuted instantiation: _ZN15ImSpanAllocatorILi6EE7GetSpanIsEEviP6ImSpanIT_E
Unexecuted instantiation: _ZN15ImSpanAllocatorILi6EE7GetSpanI18ImGuiTableCellDataEEviP6ImSpanIT_E
712
};
713
714
// Helper: ImStableVector<>
715
// Allocating chunks of BLOCK_SIZE items. Objects pointers are never invalidated when growing, only by clear().
716
// Important: does not destruct anything!
717
// Implemented only the minimum set of functions we need for it.
718
template<typename T, int BLOCK_SIZE>
719
struct ImStableVector
720
{
721
    int                 Size = 0;
722
    int                 Capacity = 0;
723
    ImVector<T*>        Blocks;
724
725
    // Functions
726
1
    inline ~ImStableVector()                        { for (T* block : Blocks) IM_FREE(block); }
727
728
    inline void         clear()                     { Size = Capacity = 0; Blocks.clear_delete(); }
729
    inline void         resize(int new_size)        { if (new_size > Capacity) reserve(new_size); Size = new_size; }
730
    inline void         reserve(int new_cap)
731
1
    {
732
1
        new_cap = IM_MEMALIGN(new_cap, BLOCK_SIZE);
733
1
        int old_count = Capacity / BLOCK_SIZE;
734
1
        int new_count = new_cap / BLOCK_SIZE;
735
1
        if (new_count <= old_count)
736
0
            return;
737
1
        Blocks.resize(new_count);
738
2
        for (int n = old_count; n < new_count; n++)
739
1
            Blocks[n] = (T*)IM_ALLOC(sizeof(T) * BLOCK_SIZE);
740
1
        Capacity = new_cap;
741
1
    }
742
1
    inline T&           operator[](int i)           { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
743
    inline const T&     operator[](int i) const     { IM_ASSERT(i >= 0 && i < Size); return Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; }
744
1
    inline T*           push_back(const T& v)       { int i = Size; IM_ASSERT(i >= 0); if (Size == Capacity) reserve(Capacity + BLOCK_SIZE); void* ptr = &Blocks[i / BLOCK_SIZE][i % BLOCK_SIZE]; memcpy(ptr, &v, sizeof(v)); Size++; return (T*)ptr; }
745
};
746
747
// Helper: ImPool<>
748
// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,
749
// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.
750
typedef int ImPoolIdx;
751
template<typename T>
752
struct ImPool
753
{
754
    ImVector<T>     Buf;        // Contiguous data
755
    ImGuiStorage    Map;        // ID->Index
756
    ImPoolIdx       FreeIdx;    // Next free idx to use
757
    ImPoolIdx       AliveCount; // Number of active/alive items (for display purpose)
758
759
3
    ImPool()    { FreeIdx = AliveCount = 0; }
_ZN6ImPoolI10ImGuiTableEC2Ev
Line
Count
Source
759
1
    ImPool()    { FreeIdx = AliveCount = 0; }
_ZN6ImPoolI11ImGuiTabBarEC2Ev
Line
Count
Source
759
1
    ImPool()    { FreeIdx = AliveCount = 0; }
_ZN6ImPoolI21ImGuiMultiSelectStateEC2Ev
Line
Count
Source
759
1
    ImPool()    { FreeIdx = AliveCount = 0; }
760
3
    ~ImPool()   { Clear(); }
_ZN6ImPoolI10ImGuiTableED2Ev
Line
Count
Source
760
1
    ~ImPool()   { Clear(); }
_ZN6ImPoolI11ImGuiTabBarED2Ev
Line
Count
Source
760
1
    ~ImPool()   { Clear(); }
_ZN6ImPoolI21ImGuiMultiSelectStateED2Ev
Line
Count
Source
760
1
    ~ImPool()   { Clear(); }
761
0
    T*          GetByKey(ImGuiID key)               { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; }
Unexecuted instantiation: _ZN6ImPoolI21ImGuiMultiSelectStateE8GetByKeyEj
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE8GetByKeyEj
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE8GetByKeyEj
762
0
    T*          GetByIndex(ImPoolIdx n)             { return &Buf[n]; }
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE10GetByIndexEi
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE10GetByIndexEi
Unexecuted instantiation: _ZN6ImPoolI21ImGuiMultiSelectStateE10GetByIndexEi
763
0
    ImPoolIdx   GetIndex(const T* p) const          { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); }
Unexecuted instantiation: _ZNK6ImPoolI10ImGuiTableE8GetIndexEPKS0_
Unexecuted instantiation: _ZNK6ImPoolI11ImGuiTabBarE8GetIndexEPKS0_
764
0
    T*          GetOrAddByKey(ImGuiID key)          { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); }
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE13GetOrAddByKeyEj
Unexecuted instantiation: _ZN6ImPoolI21ImGuiMultiSelectStateE13GetOrAddByKeyEj
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE13GetOrAddByKeyEj
765
0
    bool        Contains(const T* p) const          { return (p >= Buf.Data && p < Buf.Data + Buf.Size); }
766
6
    void        Clear()                             { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }
_ZN6ImPoolI11ImGuiTabBarE5ClearEv
Line
Count
Source
766
2
    void        Clear()                             { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }
_ZN6ImPoolI10ImGuiTableE5ClearEv
Line
Count
Source
766
2
    void        Clear()                             { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }
_ZN6ImPoolI21ImGuiMultiSelectStateE5ClearEv
Line
Count
Source
766
2
    void        Clear()                             { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }
767
0
    T*          Add()                               { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; }
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE3AddEv
Unexecuted instantiation: _ZN6ImPoolI21ImGuiMultiSelectStateE3AddEv
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE3AddEv
768
0
    void        Remove(ImGuiID key, const T* p)     { Remove(key, GetIndex(p)); }
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE6RemoveEjPKS0_
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE6RemoveEjPKS0_
769
0
    void        Remove(ImGuiID key, ImPoolIdx idx)  { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; }
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE6RemoveEji
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE6RemoveEji
770
    void        Reserve(int capacity)               { Buf.reserve(capacity); Map.Data.reserve(capacity); }
771
772
    // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... }
773
    // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize()
774
0
    int         GetAliveCount() const               { return AliveCount; }      // Number of active/alive items in the pool (for display purpose)
Unexecuted instantiation: _ZNK6ImPoolI11ImGuiTabBarE13GetAliveCountEv
Unexecuted instantiation: _ZNK6ImPoolI10ImGuiTableE13GetAliveCountEv
Unexecuted instantiation: _ZNK6ImPoolI21ImGuiMultiSelectStateE13GetAliveCountEv
775
    int         GetBufSize() const                  { return Buf.Size; }
776
1
    int         GetMapSize() const                  { return Map.Data.Size; }   // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere
_ZNK6ImPoolI10ImGuiTableE10GetMapSizeEv
Line
Count
Source
776
1
    int         GetMapSize() const                  { return Map.Data.Size; }   // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere
Unexecuted instantiation: _ZNK6ImPoolI11ImGuiTabBarE10GetMapSizeEv
Unexecuted instantiation: _ZNK6ImPoolI21ImGuiMultiSelectStateE10GetMapSizeEv
777
0
    T*          TryGetMapData(ImPoolIdx n)          { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); }
Unexecuted instantiation: _ZN6ImPoolI10ImGuiTableE13TryGetMapDataEi
Unexecuted instantiation: _ZN6ImPoolI11ImGuiTabBarE13TryGetMapDataEi
Unexecuted instantiation: _ZN6ImPoolI21ImGuiMultiSelectStateE13TryGetMapDataEi
778
};
779
780
// Helper: ImChunkStream<>
781
// Build and iterate a contiguous stream of variable-sized structures.
782
// This is used by Settings to store persistent data while reducing allocation count.
783
// We store the chunk size first, and align the final size on 4 bytes boundaries.
784
// The tedious/zealous amount of casting is to avoid -Wcast-align warnings.
785
template<typename T>
786
struct ImChunkStream
787
{
788
    ImVector<char>  Buf;
789
790
1
    void    clear()                     { Buf.clear(); }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE5clearEv
Line
Count
Source
790
1
    void    clear()                     { Buf.clear(); }
Unexecuted instantiation: _ZN13ImChunkStreamI18ImGuiTableSettingsE5clearEv
791
1
    bool    empty() const               { return Buf.Size == 0; }
792
1
    int     size() const                { return Buf.Size; }
_ZNK13ImChunkStreamI19ImGuiWindowSettingsE4sizeEv
Line
Count
Source
792
1
    int     size() const                { return Buf.Size; }
Unexecuted instantiation: _ZNK13ImChunkStreamI18ImGuiTableSettingsE4sizeEv
793
29
    T*      alloc_chunk(size_t sz)      { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE11alloc_chunkEm
Line
Count
Source
793
3
    T*      alloc_chunk(size_t sz)      { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); }
_ZN13ImChunkStreamI18ImGuiTableSettingsE11alloc_chunkEm
Line
Count
Source
793
26
    T*      alloc_chunk(size_t sz)      { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); }
794
394
    T*      begin()                     { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE5beginEv
Line
Count
Source
794
16
    T*      begin()                     { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); }
_ZN13ImChunkStreamI18ImGuiTableSettingsE5beginEv
Line
Count
Source
794
378
    T*      begin()                     { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); }
795
360
    T*      next_chunk(T* p)            { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE10next_chunkEPS0_
Line
Count
Source
795
9
    T*      next_chunk(T* p)            { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; }
_ZN13ImChunkStreamI18ImGuiTableSettingsE10next_chunkEPS0_
Line
Count
Source
795
351
    T*      next_chunk(T* p)            { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; }
796
360
    int     chunk_size(const T* p)      { return ((const int*)p)[-1]; }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE10chunk_sizeEPKS0_
Line
Count
Source
796
9
    int     chunk_size(const T* p)      { return ((const int*)p)[-1]; }
_ZN13ImChunkStreamI18ImGuiTableSettingsE10chunk_sizeEPKS0_
Line
Count
Source
796
351
    int     chunk_size(const T* p)      { return ((const int*)p)[-1]; }
797
1.05k
    T*      end()                       { return (T*)(void*)(Buf.Data + Buf.Size); }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE3endEv
Line
Count
Source
797
24
    T*      end()                       { return (T*)(void*)(Buf.Data + Buf.Size); }
_ZN13ImChunkStreamI18ImGuiTableSettingsE3endEv
Line
Count
Source
797
1.02k
    T*      end()                       { return (T*)(void*)(Buf.Data + Buf.Size); }
798
1
    int     offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE15offset_from_ptrEPKS0_
Line
Count
Source
798
1
    int     offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }
Unexecuted instantiation: _ZN13ImChunkStreamI18ImGuiTableSettingsE15offset_from_ptrEPKS0_
799
1
    T*      ptr_from_offset(int off)    { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }
_ZN13ImChunkStreamI19ImGuiWindowSettingsE15ptr_from_offsetEi
Line
Count
Source
799
1
    T*      ptr_from_offset(int off)    { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }
Unexecuted instantiation: _ZN13ImChunkStreamI18ImGuiTableSettingsE15ptr_from_offsetEi
800
0
    void    swap(ImChunkStream<T>& rhs) { rhs.Buf.swap(Buf); }
801
};
802
803
// Helper: ImGuiTextIndex
804
// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.
805
struct ImGuiTextIndex
806
{
807
    ImVector<int>   Offsets;
808
    int             EndOffset = 0;                          // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)
809
810
2
    void            clear()                                 { Offsets.clear(); EndOffset = 0; }
811
0
    int             size()                                  { return Offsets.Size; }
812
0
    const char*     get_line_begin(const char* base, int n) { return base + (Offsets.Size != 0 ? Offsets[n] : 0); }
813
0
    const char*     get_line_end(const char* base, int n)   { return base + (n + 1 < Offsets.Size ? (Offsets[n + 1] - 1) : EndOffset); }
814
    void            append(const char* base, int old_size, int new_size);
815
};
816
817
// Helper: ImGuiStorage
818
IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key);
819
820
//-----------------------------------------------------------------------------
821
// [SECTION] ImDrawList support
822
//-----------------------------------------------------------------------------
823
824
// ImDrawList: Helper function to calculate a circle's segment count given its radius and a "maximum error" value.
825
// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693
826
// Number of segments (N) is calculated using equation:
827
//   N = ceil ( pi / acos(1 - error / r) )     where r > 0, error <= r
828
// Our equation is significantly simpler that one in the post thanks for choosing segment that is
829
// perpendicular to X axis. Follow steps in the article from this starting condition and you will
830
// will get this result.
831
//
832
// Rendering circles with an odd number of segments, while mathematically correct will produce
833
// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.)
834
63
#define IM_ROUNDUP_TO_EVEN(_V)                                  ((((_V) + 1) / 2) * 2)
835
63
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     4
836
63
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512
837
63
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)
838
839
// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'.
840
2
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR)    ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))))
841
#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD)     ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD))
842
843
// ImDrawList: Lookup table size for adaptive arc drawing, cover full circle.
844
#ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE
845
1
#define IM_DRAWLIST_ARCFAST_TABLE_SIZE                          48 // Number of samples in lookup table.
846
#endif
847
1
#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX                          IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle.
848
849
// Data shared between all ImDrawList instances
850
// Conceptually this could have been called e.g. ImDrawListSharedContext
851
// Typically one ImGui context would create and maintain one of this.
852
// You may want to create your own instance of you try to ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.
853
struct IMGUI_API ImDrawListSharedData
854
{
855
    ImVec2          TexUvWhitePixel;            // UV of white pixel in the atlas (== FontAtlas->TexUvWhitePixel)
856
    const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas (== FontAtlas->TexUvLines)
857
    ImFontAtlas*    FontAtlas;                  // Current font atlas
858
    ImFont*         Font;                       // Current font (used for simplified AddText overload)
859
    float           FontSize;                   // Current font size (used for for simplified AddText overload)
860
    float           FontScale;                  // Current font scale (== FontSize / Font->FontSize)
861
    float           CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo()
862
    float           CircleSegmentMaxError;      // Number of circle segments to use per pixel of radius for AddCircle() etc
863
    float           InitialFringeScale;         // Initial scale to apply to AA fringe
864
    ImDrawListFlags InitialFlags;               // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)
865
    ImVec4          ClipRectFullscreen;         // Value for PushClipRectFullscreen()
866
    ImVector<ImVec2> TempBuffer;                // Temporary write buffer
867
    ImVector<ImDrawList*> DrawLists;            // All draw lists associated to this ImDrawListSharedData
868
    ImGuiContext*   Context;                    // [OPTIONAL] Link to Dear ImGui context. 99% of ImDrawList/ImFontAtlas can function without an ImGui context, but this facilitate handling one legacy edge case.
869
870
    // Lookup tables
871
    ImVec2          ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.
872
    float           ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo()
873
    ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)
874
875
    ImDrawListSharedData();
876
    ~ImDrawListSharedData();
877
    void SetCircleTessellationMaxError(float max_error);
878
};
879
880
struct ImDrawDataBuilder
881
{
882
    ImVector<ImDrawList*>*  Layers[2];      // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.
883
    ImVector<ImDrawList*>   LayerData1;
884
885
1
    ImDrawDataBuilder()                     { memset(this, 0, sizeof(*this)); }
886
};
887
888
struct ImFontStackData
889
{
890
    ImFont*     Font;
891
    float       FontSizeBeforeScaling;      // ~~ style.FontSizeBase
892
    float       FontSizeAfterScaling;       // ~~ g.FontSize
893
};
894
895
//-----------------------------------------------------------------------------
896
// [SECTION] Style support
897
//-----------------------------------------------------------------------------
898
899
struct ImGuiStyleVarInfo
900
{
901
    ImU32           Count : 8;      // 1+
902
    ImGuiDataType   DataType : 8;
903
    ImU32           Offset : 16;    // Offset in parent structure
904
807k
    void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }
905
};
906
907
// Stacked color modifier, backup of modified data so we can restore it
908
struct ImGuiColorMod
909
{
910
    ImGuiCol        Col;
911
    ImVec4          BackupValue;
912
};
913
914
// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.
915
struct ImGuiStyleMod
916
{
917
    ImGuiStyleVar   VarIdx;
918
    union           { int BackupInt[2]; float BackupFloat[2]; };
919
0
    ImGuiStyleMod(ImGuiStyleVar idx, int v)     { VarIdx = idx; BackupInt[0] = v; }
920
81.2k
    ImGuiStyleMod(ImGuiStyleVar idx, float v)   { VarIdx = idx; BackupFloat[0] = v; }
921
322k
    ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v)  { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }
922
};
923
924
//-----------------------------------------------------------------------------
925
// [SECTION] Data types support
926
//-----------------------------------------------------------------------------
927
928
struct ImGuiDataTypeStorage
929
{
930
    ImU8        Data[8];        // Opaque storage to fit any data up to ImGuiDataType_COUNT
931
};
932
933
// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().
934
struct ImGuiDataTypeInfo
935
{
936
    size_t      Size;           // Size in bytes
937
    const char* Name;           // Short descriptive name for the type, for debugging
938
    const char* PrintFmt;       // Default printf format for the type
939
    const char* ScanFmt;        // Default scanf format for the type
940
};
941
942
// Extend ImGuiDataType_
943
enum ImGuiDataTypePrivate_
944
{
945
    ImGuiDataType_Pointer = ImGuiDataType_COUNT,
946
    ImGuiDataType_ID,
947
};
948
949
//-----------------------------------------------------------------------------
950
// [SECTION] Widgets support: flags, enums, data structures
951
//-----------------------------------------------------------------------------
952
953
// Extend ImGuiItemFlags
954
// - input: PushItemFlag() manipulates g.CurrentItemFlags, g.NextItemData.ItemFlags, ItemAdd() calls may add extra flags too.
955
// - output: stored in g.LastItemData.ItemFlags
956
enum ImGuiItemFlagsPrivate_
957
{
958
    // Controlled by user
959
    ImGuiItemFlags_Disabled                 = 1 << 10, // false     // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211).
960
    ImGuiItemFlags_ReadOnly                 = 1 << 11, // false     // [ALPHA] Allow hovering interactions but underlying value is not changed.
961
    ImGuiItemFlags_MixedValue               = 1 << 12, // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)
962
    ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 13, // false     // Disable hoverable check in ItemHoverable()
963
    ImGuiItemFlags_AllowOverlap             = 1 << 14, // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.
964
    ImGuiItemFlags_NoNavDisableMouseHover   = 1 << 15, // false     // Nav keyboard/gamepad mode doesn't disable hover highlight (behave as if NavHighlightItemUnderNav==false).
965
    ImGuiItemFlags_NoMarkEdited             = 1 << 16, // false     // Skip calling MarkItemEdited()
966
    ImGuiItemFlags_NoFocus                  = 1 << 17, // false     // [EXPERIMENTAL: Not very well specced] Clicking doesn't take focus. Automatically sets ImGuiButtonFlags_NoFocus + ImGuiButtonFlags_NoNavFocus in ButtonBehavior().
967
968
    // Controlled by widget code
969
    ImGuiItemFlags_Inputable                = 1 << 20, // false     // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.
970
    ImGuiItemFlags_HasSelectionUserData     = 1 << 21, // false     // Set by SetNextItemSelectionUserData()
971
    ImGuiItemFlags_IsMultiSelect            = 1 << 22, // false     // Set by SetNextItemSelectionUserData()
972
973
    ImGuiItemFlags_Default_                 = ImGuiItemFlags_AutoClosePopups,    // Please don't change, use PushItemFlag() instead.
974
975
    // Obsolete
976
    //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior
977
};
978
979
// Status flags for an already submitted item
980
// - output: stored in g.LastItemData.StatusFlags
981
enum ImGuiItemStatusFlags_
982
{
983
    ImGuiItemStatusFlags_None               = 0,
984
    ImGuiItemStatusFlags_HoveredRect        = 1 << 0,   // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)
985
    ImGuiItemStatusFlags_HasDisplayRect     = 1 << 1,   // g.LastItemData.DisplayRect is valid
986
    ImGuiItemStatusFlags_Edited             = 1 << 2,   // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)
987
    ImGuiItemStatusFlags_ToggledSelection   = 1 << 3,   // Set when Selectable(), TreeNode() reports toggling a selection. We can't report "Selected", only state changes, in order to easily handle clipping with less issues.
988
    ImGuiItemStatusFlags_ToggledOpen        = 1 << 4,   // Set when TreeNode() reports toggling their open state.
989
    ImGuiItemStatusFlags_HasDeactivated     = 1 << 5,   // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.
990
    ImGuiItemStatusFlags_Deactivated        = 1 << 6,   // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.
991
    ImGuiItemStatusFlags_HoveredWindow      = 1 << 7,   // Override the HoveredWindow test to allow cross-window hover testing.
992
    ImGuiItemStatusFlags_Visible            = 1 << 8,   // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).
993
    ImGuiItemStatusFlags_HasClipRect        = 1 << 9,   // g.LastItemData.ClipRect is valid.
994
    ImGuiItemStatusFlags_HasShortcut        = 1 << 10,  // g.LastItemData.Shortcut valid. Set by SetNextItemShortcut() -> ItemAdd().
995
    //ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8,   // Removed IN 1.90.1 (Dec 2023). The trigger is part of g.NavActivateId. See commit 54c1bdeceb.
996
997
    // Additional status + semantic for ImGuiTestEngine
998
#ifdef IMGUI_ENABLE_TEST_ENGINE
999
    ImGuiItemStatusFlags_Openable           = 1 << 20,  // Item is an openable (e.g. TreeNode)
1000
    ImGuiItemStatusFlags_Opened             = 1 << 21,  // Opened status
1001
    ImGuiItemStatusFlags_Checkable          = 1 << 22,  // Item is a checkable (e.g. CheckBox, MenuItem)
1002
    ImGuiItemStatusFlags_Checked            = 1 << 23,  // Checked status
1003
    ImGuiItemStatusFlags_Inputable          = 1 << 24,  // Item is a text-inputable (e.g. InputText, SliderXXX, DragXXX)
1004
#endif
1005
};
1006
1007
// Extend ImGuiHoveredFlags_
1008
enum ImGuiHoveredFlagsPrivate_
1009
{
1010
    ImGuiHoveredFlags_DelayMask_                    = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay,
1011
    ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary,
1012
    ImGuiHoveredFlags_AllowedMaskForIsItemHovered   = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_,
1013
};
1014
1015
// Extend ImGuiInputTextFlags_
1016
enum ImGuiInputTextFlagsPrivate_
1017
{
1018
    // [Internal]
1019
    ImGuiInputTextFlags_Multiline           = 1 << 26,  // For internal use by InputTextMultiline()
1020
    ImGuiInputTextFlags_MergedItem          = 1 << 27,  // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.
1021
    ImGuiInputTextFlags_LocalizeDecimalPoint= 1 << 28,  // For internal use by InputScalar() and TempInputScalar()
1022
};
1023
1024
// Extend ImGuiButtonFlags_
1025
enum ImGuiButtonFlagsPrivate_
1026
{
1027
    ImGuiButtonFlags_PressedOnClick         = 1 << 4,   // return true on click (mouse down event)
1028
    ImGuiButtonFlags_PressedOnClickRelease  = 1 << 5,   // [Default] return true on click + release on same item <-- this is what the majority of Button are using
1029
    ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item
1030
    ImGuiButtonFlags_PressedOnRelease       = 1 << 7,   // return true on release (default requires click+release)
1031
    ImGuiButtonFlags_PressedOnDoubleClick   = 1 << 8,   // return true on double-click (default requires click+release)
1032
    ImGuiButtonFlags_PressedOnDragDropHold  = 1 << 9,   // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)
1033
    //ImGuiButtonFlags_Repeat               = 1 << 10,  // hold to repeat -> use ImGuiItemFlags_ButtonRepeat instead.
1034
    ImGuiButtonFlags_FlattenChildren        = 1 << 11,  // allow interactions even if a child window is overlapping
1035
    ImGuiButtonFlags_AllowOverlap           = 1 << 12,  // require previous frame HoveredId to either match id or be null before being usable.
1036
    //ImGuiButtonFlags_DontClosePopups      = 1 << 13,  // disable automatically closing parent popup on press
1037
    //ImGuiButtonFlags_Disabled             = 1 << 14,  // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled
1038
    ImGuiButtonFlags_AlignTextBaseLine      = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine
1039
    ImGuiButtonFlags_NoKeyModsAllowed       = 1 << 16,  // disable mouse interaction if a key modifier is held
1040
    ImGuiButtonFlags_NoHoldingActiveId      = 1 << 17,  // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)
1041
    ImGuiButtonFlags_NoNavFocus             = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used every time an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.ItemFlags)
1042
    ImGuiButtonFlags_NoHoveredOnFocus       = 1 << 19,  // don't report as hovered when nav focus is on this item
1043
    ImGuiButtonFlags_NoSetKeyOwner          = 1 << 20,  // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
1044
    ImGuiButtonFlags_NoTestKeyOwner         = 1 << 21,  // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
1045
    ImGuiButtonFlags_NoFocus                = 1 << 22,  // [EXPERIMENTAL: Not very well specced]. Don't focus parent window when clicking.
1046
    ImGuiButtonFlags_PressedOnMask_         = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,
1047
    ImGuiButtonFlags_PressedOnDefault_      = ImGuiButtonFlags_PressedOnClickRelease,
1048
    //ImGuiButtonFlags_NoKeyModifiers       = ImGuiButtonFlags_NoKeyModsAllowed, // Renamed in 1.91.4
1049
};
1050
1051
// Extend ImGuiComboFlags_
1052
enum ImGuiComboFlagsPrivate_
1053
{
1054
    ImGuiComboFlags_CustomPreview           = 1 << 20,  // enable BeginComboPreview()
1055
};
1056
1057
// Extend ImGuiSliderFlags_
1058
enum ImGuiSliderFlagsPrivate_
1059
{
1060
    ImGuiSliderFlags_Vertical               = 1 << 20,  // Should this slider be orientated vertically?
1061
    ImGuiSliderFlags_ReadOnly               = 1 << 21,  // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead.
1062
};
1063
1064
// Extend ImGuiSelectableFlags_
1065
enum ImGuiSelectableFlagsPrivate_
1066
{
1067
    // NB: need to be in sync with last value of ImGuiSelectableFlags_
1068
    ImGuiSelectableFlags_NoHoldingActiveID      = 1 << 20,
1069
    ImGuiSelectableFlags_SelectOnClick          = 1 << 22,  // Override button behavior to react on Click (default is Click+Release)
1070
    ImGuiSelectableFlags_SelectOnRelease        = 1 << 23,  // Override button behavior to react on Release (default is Click+Release)
1071
    ImGuiSelectableFlags_SpanAvailWidth         = 1 << 24,  // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)
1072
    ImGuiSelectableFlags_SetNavIdOnHover        = 1 << 25,  // Set Nav/Focus ID on mouse hover (used by MenuItem)
1073
    ImGuiSelectableFlags_NoPadWithHalfSpacing   = 1 << 26,  // Disable padding each side with ItemSpacing * 0.5f
1074
    ImGuiSelectableFlags_NoSetKeyOwner          = 1 << 27,  // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)
1075
};
1076
1077
// Extend ImGuiTreeNodeFlags_
1078
enum ImGuiTreeNodeFlagsPrivate_
1079
{
1080
    ImGuiTreeNodeFlags_NoNavFocus                 = 1 << 27,// Don't claim nav focus when interacting with this item (#8551)
1081
    ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 28,// FIXME-WIP: Hard-coded for CollapsingHeader()
1082
    ImGuiTreeNodeFlags_UpsideDownArrow            = 1 << 29,// FIXME-WIP: Turn Down arrow into an Up arrow, for reversed trees (#6517)
1083
    ImGuiTreeNodeFlags_OpenOnMask_                = ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_OpenOnArrow,
1084
    ImGuiTreeNodeFlags_DrawLinesMask_             = ImGuiTreeNodeFlags_DrawLinesNone | ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes,
1085
};
1086
1087
enum ImGuiSeparatorFlags_
1088
{
1089
    ImGuiSeparatorFlags_None                    = 0,
1090
    ImGuiSeparatorFlags_Horizontal              = 1 << 0,   // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar
1091
    ImGuiSeparatorFlags_Vertical                = 1 << 1,
1092
    ImGuiSeparatorFlags_SpanAllColumns          = 1 << 2,   // Make separator cover all columns of a legacy Columns() set.
1093
};
1094
1095
// Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags.
1096
// FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf()
1097
// and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in.
1098
enum ImGuiFocusRequestFlags_
1099
{
1100
    ImGuiFocusRequestFlags_None                 = 0,
1101
    ImGuiFocusRequestFlags_RestoreFocusedChild  = 1 << 0,   // Find last focused child (if any) and focus it instead.
1102
    ImGuiFocusRequestFlags_UnlessBelowModal     = 1 << 1,   // Do not set focus if the window is below a modal.
1103
};
1104
1105
enum ImGuiTextFlags_
1106
{
1107
    ImGuiTextFlags_None                         = 0,
1108
    ImGuiTextFlags_NoWidthForLargeClippedText   = 1 << 0,
1109
};
1110
1111
enum ImGuiTooltipFlags_
1112
{
1113
    ImGuiTooltipFlags_None                      = 0,
1114
    ImGuiTooltipFlags_OverridePrevious          = 1 << 1,   // Clear/ignore previously submitted tooltip (defaults to append)
1115
};
1116
1117
// FIXME: this is in development, not exposed/functional as a generic feature yet.
1118
// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2
1119
enum ImGuiLayoutType_
1120
{
1121
    ImGuiLayoutType_Horizontal = 0,
1122
    ImGuiLayoutType_Vertical = 1
1123
};
1124
1125
// Flags for LogBegin() text capturing function
1126
enum ImGuiLogFlags_
1127
{
1128
    ImGuiLogFlags_None = 0,
1129
1130
    ImGuiLogFlags_OutputTTY         = 1 << 0,
1131
    ImGuiLogFlags_OutputFile        = 1 << 1,
1132
    ImGuiLogFlags_OutputBuffer      = 1 << 2,
1133
    ImGuiLogFlags_OutputClipboard   = 1 << 3,
1134
    ImGuiLogFlags_OutputMask_       = ImGuiLogFlags_OutputTTY | ImGuiLogFlags_OutputFile | ImGuiLogFlags_OutputBuffer | ImGuiLogFlags_OutputClipboard,
1135
};
1136
1137
// X/Y enums are fixed to 0/1 so they may be used to index ImVec2
1138
enum ImGuiAxis
1139
{
1140
    ImGuiAxis_None = -1,
1141
    ImGuiAxis_X = 0,
1142
    ImGuiAxis_Y = 1
1143
};
1144
1145
enum ImGuiPlotType
1146
{
1147
    ImGuiPlotType_Lines,
1148
    ImGuiPlotType_Histogram,
1149
};
1150
1151
// Storage data for BeginComboPreview()/EndComboPreview()
1152
struct IMGUI_API ImGuiComboPreviewData
1153
{
1154
    ImRect          PreviewRect;
1155
    ImVec2          BackupCursorPos;
1156
    ImVec2          BackupCursorMaxPos;
1157
    ImVec2          BackupCursorPosPrevLine;
1158
    float           BackupPrevLineTextBaseOffset;
1159
    ImGuiLayoutType BackupLayout;
1160
1161
1
    ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); }
1162
};
1163
1164
// Stacked storage data for BeginGroup()/EndGroup()
1165
struct IMGUI_API ImGuiGroupData
1166
{
1167
    ImGuiID     WindowID;
1168
    ImVec2      BackupCursorPos;
1169
    ImVec2      BackupCursorMaxPos;
1170
    ImVec2      BackupCursorPosPrevLine;
1171
    ImVec1      BackupIndent;
1172
    ImVec1      BackupGroupOffset;
1173
    ImVec2      BackupCurrLineSize;
1174
    float       BackupCurrLineTextBaseOffset;
1175
    ImGuiID     BackupActiveIdIsAlive;
1176
    bool        BackupDeactivatedIdIsAlive;
1177
    bool        BackupHoveredIdIsAlive;
1178
    bool        BackupIsSameLine;
1179
    bool        EmitItem;
1180
};
1181
1182
// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper.
1183
struct IMGUI_API ImGuiMenuColumns
1184
{
1185
    ImU32       TotalWidth;
1186
    ImU32       NextTotalWidth;
1187
    ImU16       Spacing;
1188
    ImU16       OffsetIcon;         // Always zero for now
1189
    ImU16       OffsetLabel;        // Offsets are locked in Update()
1190
    ImU16       OffsetShortcut;
1191
    ImU16       OffsetMark;
1192
    ImU16       Widths[4];          // Width of:   Icon, Label, Shortcut, Mark  (accumulators for current frame)
1193
1194
3
    ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); }
1195
    void        Update(float spacing, bool window_reappearing);
1196
    float       DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark);
1197
    void        CalcNextTotalWidth(bool update_offsets);
1198
};
1199
1200
// Internal temporary state for deactivating InputText() instances.
1201
struct IMGUI_API ImGuiInputTextDeactivatedState
1202
{
1203
    ImGuiID            ID;              // widget id owning the text state (which just got deactivated)
1204
    ImVector<char>     TextA;           // text buffer
1205
1206
1
    ImGuiInputTextDeactivatedState()    { memset(this, 0, sizeof(*this)); }
1207
1
    void    ClearFreeMemory()           { ID = 0; TextA.clear(); }
1208
};
1209
1210
// Forward declare imstb_textedit.h structure + make its main configuration define accessible
1211
#undef IMSTB_TEXTEDIT_STRING
1212
#undef IMSTB_TEXTEDIT_CHARTYPE
1213
#define IMSTB_TEXTEDIT_STRING             ImGuiInputTextState
1214
0
#define IMSTB_TEXTEDIT_CHARTYPE           char
1215
0
#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE   (-1.0f)
1216
0
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT     99
1217
0
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT      999
1218
namespace ImStb { struct STB_TexteditState; }
1219
typedef ImStb::STB_TexteditState ImStbTexteditState;
1220
1221
// Internal state of the currently focused/edited text input box
1222
// For a given item ID, access with ImGui::GetInputTextState()
1223
struct IMGUI_API ImGuiInputTextState
1224
{
1225
    ImGuiContext*           Ctx;                    // parent UI context (needs to be set explicitly by parent).
1226
    ImStbTexteditState*     Stb;                    // State for stb_textedit.h
1227
    ImGuiInputTextFlags     Flags;                  // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.
1228
    ImGuiID                 ID;                     // widget id owning the text state
1229
    int                     TextLen;                // UTF-8 length of the string in TextA (in bytes)
1230
    const char*             TextSrc;                // == TextA.Data unless read-only, in which case == buf passed to InputText(). Field only set and valid _inside_ the call InputText() call.
1231
    ImVector<char>          TextA;                  // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1).
1232
    ImVector<char>          TextToRevertTo;         // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered)
1233
    ImVector<char>          CallbackTextBackup;     // temporary storage for callback to support automatic reconcile of undo-stack
1234
    int                     BufCapacity;            // end-user buffer capacity (include zero terminator)
1235
    ImVec2                  Scroll;                 // horizontal offset (managed manually) + vertical scrolling (pulled from child window's own Scroll.y)
1236
    int                     LineCount;              // last line count (solely for debugging)
1237
    float                   WrapWidth;              // word-wrapping width
1238
    float                   CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately
1239
    bool                    CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)
1240
    bool                    CursorCenterY;          // set when we want scrolling to be centered over the cursor position (while resizing a word-wrapping field)
1241
    bool                    SelectedAllMouseLock;   // after a double-click to select all, we ignore further mouse drags to update selection
1242
    bool                    Edited;                 // edited this frame
1243
    bool                    WantReloadUserBuf;      // force a reload of user buf so it may be modified externally. may be automatic in future version.
1244
    ImS8                    LastMoveDirectionLR;    // ImGuiDir_Left or ImGuiDir_Right. track last movement direction so when cursor cross over a word-wrapping boundaries we can display it on either line depending on last move.s
1245
    int                     ReloadSelectionStart;
1246
    int                     ReloadSelectionEnd;
1247
1248
    ImGuiInputTextState();
1249
    ~ImGuiInputTextState();
1250
0
    void        ClearText()                 { TextLen = 0; TextA[0] = 0; CursorClamp(); }
1251
1
    void        ClearFreeMemory()           { TextA.clear(); TextToRevertTo.clear(); }
1252
    void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation
1253
    void        OnCharPressed(unsigned int c);
1254
    float       GetPreferredOffsetX() const;
1255
1256
    // Cursor & Selection
1257
    void        CursorAnimReset();
1258
    void        CursorClamp();
1259
    bool        HasSelection() const;
1260
    void        ClearSelection();
1261
    int         GetCursorPos() const;
1262
    int         GetSelectionStart() const;
1263
    int         GetSelectionEnd() const;
1264
    void        SelectAll();
1265
1266
    // Reload user buf (WIP #2890)
1267
    // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this)
1268
    //   strcpy(my_buf, "hello");
1269
    //   if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item
1270
    //       state->ReloadUserBufAndSelectAll();
1271
    void        ReloadUserBufAndSelectAll();
1272
    void        ReloadUserBufAndKeepSelection();
1273
    void        ReloadUserBufAndMoveToEnd();
1274
};
1275
1276
enum ImGuiWindowRefreshFlags_
1277
{
1278
    ImGuiWindowRefreshFlags_None                = 0,
1279
    ImGuiWindowRefreshFlags_TryToAvoidRefresh   = 1 << 0,   // [EXPERIMENTAL] Try to keep existing contents, USER MUST NOT HONOR BEGIN() RETURNING FALSE AND NOT APPEND.
1280
    ImGuiWindowRefreshFlags_RefreshOnHover      = 1 << 1,   // [EXPERIMENTAL] Always refresh on hover
1281
    ImGuiWindowRefreshFlags_RefreshOnFocus      = 1 << 2,   // [EXPERIMENTAL] Always refresh on focus
1282
    // Refresh policy/frequency, Load Balancing etc.
1283
};
1284
1285
enum ImGuiNextWindowDataFlags_
1286
{
1287
    ImGuiNextWindowDataFlags_None               = 0,
1288
    ImGuiNextWindowDataFlags_HasPos             = 1 << 0,
1289
    ImGuiNextWindowDataFlags_HasSize            = 1 << 1,
1290
    ImGuiNextWindowDataFlags_HasContentSize     = 1 << 2,
1291
    ImGuiNextWindowDataFlags_HasCollapsed       = 1 << 3,
1292
    ImGuiNextWindowDataFlags_HasSizeConstraint  = 1 << 4,
1293
    ImGuiNextWindowDataFlags_HasFocus           = 1 << 5,
1294
    ImGuiNextWindowDataFlags_HasBgAlpha         = 1 << 6,
1295
    ImGuiNextWindowDataFlags_HasScroll          = 1 << 7,
1296
    ImGuiNextWindowDataFlags_HasWindowFlags     = 1 << 8,
1297
    ImGuiNextWindowDataFlags_HasChildFlags      = 1 << 9,
1298
    ImGuiNextWindowDataFlags_HasRefreshPolicy   = 1 << 10,
1299
};
1300
1301
// Storage for SetNexWindow** functions
1302
struct ImGuiNextWindowData
1303
{
1304
    ImGuiNextWindowDataFlags    HasFlags;
1305
1306
    // Members below are NOT cleared. Always rely on HasFlags.
1307
    ImGuiCond                   PosCond;
1308
    ImGuiCond                   SizeCond;
1309
    ImGuiCond                   CollapsedCond;
1310
    ImVec2                      PosVal;
1311
    ImVec2                      PosPivotVal;
1312
    ImVec2                      SizeVal;
1313
    ImVec2                      ContentSizeVal;
1314
    ImVec2                      ScrollVal;
1315
    ImGuiWindowFlags            WindowFlags;            // Only honored by BeginTable()
1316
    ImGuiChildFlags             ChildFlags;
1317
    bool                        CollapsedVal;
1318
    ImRect                      SizeConstraintRect;
1319
    ImGuiSizeCallback           SizeCallback;
1320
    void*                       SizeCallbackUserData;
1321
    float                       BgAlphaVal;             // Override background alpha
1322
    ImVec2                      MenuBarOffsetMinVal;    // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?)
1323
    ImGuiWindowRefreshFlags     RefreshFlagsVal;
1324
1325
1
    ImGuiNextWindowData()       { memset(this, 0, sizeof(*this)); }
1326
402k
    inline void ClearFlags()    { HasFlags = ImGuiNextWindowDataFlags_None; }
1327
};
1328
1329
enum ImGuiNextItemDataFlags_
1330
{
1331
    ImGuiNextItemDataFlags_None         = 0,
1332
    ImGuiNextItemDataFlags_HasWidth     = 1 << 0,
1333
    ImGuiNextItemDataFlags_HasOpen      = 1 << 1,
1334
    ImGuiNextItemDataFlags_HasShortcut  = 1 << 2,
1335
    ImGuiNextItemDataFlags_HasRefVal    = 1 << 3,
1336
    ImGuiNextItemDataFlags_HasStorageID = 1 << 4,
1337
};
1338
1339
struct ImGuiNextItemData
1340
{
1341
    ImGuiNextItemDataFlags      HasFlags;           // Called HasFlags instead of Flags to avoid mistaking this
1342
    ImGuiItemFlags              ItemFlags;          // Currently only tested/used for ImGuiItemFlags_AllowOverlap and ImGuiItemFlags_HasSelectionUserData.
1343
1344
    // Members below are NOT cleared by ItemAdd() meaning they are still valid during e.g. NavProcessItem(). Always rely on HasFlags.
1345
    ImGuiID                     FocusScopeId;       // Set by SetNextItemSelectionUserData()
1346
    ImGuiSelectionUserData      SelectionUserData;  // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)
1347
    float                       Width;              // Set by SetNextItemWidth()
1348
    ImGuiKeyChord               Shortcut;           // Set by SetNextItemShortcut()
1349
    ImGuiInputFlags             ShortcutFlags;      // Set by SetNextItemShortcut()
1350
    bool                        OpenVal;            // Set by SetNextItemOpen()
1351
    ImU8                        OpenCond;           // Set by SetNextItemOpen()
1352
    ImGuiDataTypeStorage        RefVal;             // Not exposed yet, for ImGuiInputTextFlags_ParseEmptyAsRefVal
1353
    ImGuiID                     StorageId;          // Set by SetNextItemStorageID()
1354
1355
1
    ImGuiNextItemData()         { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }
1356
0
    inline void ClearFlags()    { HasFlags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!
1357
};
1358
1359
// Status storage for the last submitted item
1360
struct ImGuiLastItemData
1361
{
1362
    ImGuiID                 ID;
1363
    ImGuiItemFlags          ItemFlags;          // See ImGuiItemFlags_ (called 'InFlags' before v1.91.4).
1364
    ImGuiItemStatusFlags    StatusFlags;        // See ImGuiItemStatusFlags_
1365
    ImRect                  Rect;               // Full rectangle
1366
    ImRect                  NavRect;            // Navigation scoring rectangle (not displayed)
1367
    // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags are set.
1368
    ImRect                  DisplayRect;        // Display rectangle. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) is set.
1369
    ImRect                  ClipRect;           // Clip rectangle at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasClipRect) is set..
1370
    ImGuiKeyChord           Shortcut;           // Shortcut at the time of submitting item. ONLY VALID IF (StatusFlags & ImGuiItemStatusFlags_HasShortcut) is set..
1371
1372
1
    ImGuiLastItemData()     { memset(this, 0, sizeof(*this)); }
1373
};
1374
1375
// Store data emitted by TreeNode() for usage by TreePop()
1376
// - To implement ImGuiTreeNodeFlags_NavLeftJumpsToParent: store the minimum amount of data
1377
//   which we can't infer in TreePop(), to perform the equivalent of NavApplyItemToResult().
1378
//   Only stored when the node is a potential candidate for landing on a Left arrow jump.
1379
struct ImGuiTreeNodeStackData
1380
{
1381
    ImGuiID                 ID;
1382
    ImGuiTreeNodeFlags      TreeFlags;
1383
    ImGuiItemFlags          ItemFlags;      // Used for nav landing
1384
    ImRect                  NavRect;        // Used for nav landing
1385
    float                   DrawLinesX1;
1386
    float                   DrawLinesToNodesY2;
1387
    ImGuiTableColumnIdx     DrawLinesTableColumn;
1388
};
1389
1390
// sizeof() = 20
1391
struct IMGUI_API ImGuiErrorRecoveryState
1392
{
1393
    short   SizeOfWindowStack;
1394
    short   SizeOfIDStack;
1395
    short   SizeOfTreeStack;
1396
    short   SizeOfColorStack;
1397
    short   SizeOfStyleVarStack;
1398
    short   SizeOfFontStack;
1399
    short   SizeOfFocusScopeStack;
1400
    short   SizeOfGroupStack;
1401
    short   SizeOfItemFlagsStack;
1402
    short   SizeOfBeginPopupStack;
1403
    short   SizeOfDisabledStack;
1404
1405
1
    ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); }
1406
};
1407
1408
// Data saved for each window pushed into the stack
1409
struct ImGuiWindowStackData
1410
{
1411
    ImGuiWindow*            Window;
1412
    ImGuiLastItemData       ParentLastItemDataBackup;
1413
    ImGuiErrorRecoveryState StackSizesInBegin;          // Store size of various stacks for asserting
1414
    bool                    DisabledOverrideReenable;   // Non-child window override disabled flag
1415
    float                   DisabledOverrideReenableAlphaBackup;
1416
};
1417
1418
struct ImGuiShrinkWidthItem
1419
{
1420
    int         Index;
1421
    float       Width;
1422
    float       InitialWidth;
1423
};
1424
1425
struct ImGuiPtrOrIndex
1426
{
1427
    void*       Ptr;            // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool.
1428
    int         Index;          // Usually index in a main pool.
1429
1430
0
    ImGuiPtrOrIndex(void* ptr)  { Ptr = ptr; Index = -1; }
1431
0
    ImGuiPtrOrIndex(int index)  { Ptr = NULL; Index = index; }
1432
};
1433
1434
// Data used by IsItemDeactivated()/IsItemDeactivatedAfterEdit() functions
1435
struct ImGuiDeactivatedItemData
1436
{
1437
    ImGuiID     ID;
1438
    int         ElapseFrame;
1439
    bool        HasBeenEditedBefore;
1440
    bool        IsAlive;
1441
};
1442
1443
//-----------------------------------------------------------------------------
1444
// [SECTION] Popup support
1445
//-----------------------------------------------------------------------------
1446
1447
enum ImGuiPopupPositionPolicy
1448
{
1449
    ImGuiPopupPositionPolicy_Default,
1450
    ImGuiPopupPositionPolicy_ComboBox,
1451
    ImGuiPopupPositionPolicy_Tooltip,
1452
};
1453
1454
// Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack)
1455
struct ImGuiPopupData
1456
{
1457
    ImGuiID             PopupId;        // Set on OpenPopup()
1458
    ImGuiWindow*        Window;         // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()
1459
    ImGuiWindow*        RestoreNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close
1460
    int                 ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value
1461
    int                 OpenFrameCount; // Set on OpenPopup()
1462
    ImGuiID             OpenParentId;   // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)
1463
    ImVec2              OpenPopupPos;   // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)
1464
    ImVec2              OpenMousePos;   // Set on OpenPopup(), copy of mouse position at the time of opening popup
1465
1466
1
    ImGuiPopupData()    { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }
1467
};
1468
1469
//-----------------------------------------------------------------------------
1470
// [SECTION] Inputs support
1471
//-----------------------------------------------------------------------------
1472
1473
// Bit array for named keys
1474
typedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN>    ImBitArrayForNamedKeys;
1475
1476
// [Internal] Key ranges
1477
2.98k
#define ImGuiKey_LegacyNativeKey_BEGIN  0
1478
1.49k
#define ImGuiKey_LegacyNativeKey_END    512
1479
544
#define ImGuiKey_Keyboard_BEGIN         (ImGuiKey_NamedKey_BEGIN)
1480
272
#define ImGuiKey_Keyboard_END           (ImGuiKey_GamepadStart)
1481
81.6k
#define ImGuiKey_Gamepad_BEGIN          (ImGuiKey_GamepadStart)
1482
2.01M
#define ImGuiKey_Gamepad_END            (ImGuiKey_GamepadRStickDown + 1)
1483
571k
#define ImGuiKey_Mouse_BEGIN            (ImGuiKey_MouseLeft)
1484
570k
#define ImGuiKey_Mouse_END              (ImGuiKey_MouseWheelY + 1)
1485
1.14M
#define ImGuiKey_Aliases_BEGIN          (ImGuiKey_Mouse_BEGIN)
1486
570k
#define ImGuiKey_Aliases_END            (ImGuiKey_Mouse_END)
1487
1488
// [Internal] Named shortcuts for Navigation
1489
0
#define ImGuiKey_NavKeyboardTweakSlow   ImGuiMod_Ctrl
1490
0
#define ImGuiKey_NavKeyboardTweakFast   ImGuiMod_Shift
1491
0
#define ImGuiKey_NavGamepadTweakSlow    ImGuiKey_GamepadL1
1492
0
#define ImGuiKey_NavGamepadTweakFast    ImGuiKey_GamepadR1
1493
0
#define ImGuiKey_NavGamepadActivate     (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceRight : ImGuiKey_GamepadFaceDown)
1494
0
#define ImGuiKey_NavGamepadCancel       (g.IO.ConfigNavSwapGamepadButtons ? ImGuiKey_GamepadFaceDown : ImGuiKey_GamepadFaceRight)
1495
0
#define ImGuiKey_NavGamepadMenu         ImGuiKey_GamepadFaceLeft
1496
0
#define ImGuiKey_NavGamepadInput        ImGuiKey_GamepadFaceUp
1497
1498
enum ImGuiInputEventType
1499
{
1500
    ImGuiInputEventType_None = 0,
1501
    ImGuiInputEventType_MousePos,
1502
    ImGuiInputEventType_MouseWheel,
1503
    ImGuiInputEventType_MouseButton,
1504
    ImGuiInputEventType_Key,
1505
    ImGuiInputEventType_Text,
1506
    ImGuiInputEventType_Focus,
1507
    ImGuiInputEventType_COUNT
1508
};
1509
1510
enum ImGuiInputSource : int
1511
{
1512
    ImGuiInputSource_None = 0,
1513
    ImGuiInputSource_Mouse,         // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them.
1514
    ImGuiInputSource_Keyboard,
1515
    ImGuiInputSource_Gamepad,
1516
    ImGuiInputSource_COUNT
1517
};
1518
1519
// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension?
1520
// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor'
1521
struct ImGuiInputEventMousePos      { float PosX, PosY; ImGuiMouseSource MouseSource; };
1522
struct ImGuiInputEventMouseWheel    { float WheelX, WheelY; ImGuiMouseSource MouseSource; };
1523
struct ImGuiInputEventMouseButton   { int Button; bool Down; ImGuiMouseSource MouseSource; };
1524
struct ImGuiInputEventKey           { ImGuiKey Key; bool Down; float AnalogValue; };
1525
struct ImGuiInputEventText          { unsigned int Char; };
1526
struct ImGuiInputEventAppFocused    { bool Focused; };
1527
1528
struct ImGuiInputEvent
1529
{
1530
    ImGuiInputEventType             Type;
1531
    ImGuiInputSource                Source;
1532
    ImU32                           EventId;        // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data).
1533
    union
1534
    {
1535
        ImGuiInputEventMousePos     MousePos;       // if Type == ImGuiInputEventType_MousePos
1536
        ImGuiInputEventMouseWheel   MouseWheel;     // if Type == ImGuiInputEventType_MouseWheel
1537
        ImGuiInputEventMouseButton  MouseButton;    // if Type == ImGuiInputEventType_MouseButton
1538
        ImGuiInputEventKey          Key;            // if Type == ImGuiInputEventType_Key
1539
        ImGuiInputEventText         Text;           // if Type == ImGuiInputEventType_Text
1540
        ImGuiInputEventAppFocused   AppFocused;     // if Type == ImGuiInputEventType_Focus
1541
    };
1542
    bool                            AddedByTestEngine;
1543
1544
698
    ImGuiInputEvent() { memset(this, 0, sizeof(*this)); }
1545
};
1546
1547
// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.
1548
1.81M
#define ImGuiKeyOwner_Any           ((ImGuiID)0)    // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
1549
12.7M
#define ImGuiKeyOwner_NoOwner       ((ImGuiID)-1)   // Require key to have no owner.
1550
//#define ImGuiKeyOwner_None ImGuiKeyOwner_NoOwner  // We previously called this 'ImGuiKeyOwner_None' but it was inconsistent with our pattern that _None values == 0 and quite dangerous. Also using _NoOwner makes the IsKeyPressed() calls more explicit.
1551
1552
typedef ImS16 ImGuiKeyRoutingIndex;
1553
1554
// Routing table entry (sizeof() == 16 bytes)
1555
struct ImGuiKeyRoutingData
1556
{
1557
    ImGuiKeyRoutingIndex            NextEntryIndex;
1558
    ImU16                           Mods;               // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits.
1559
    ImU16                           RoutingCurrScore;   // [DEBUG] For debug display
1560
    ImU16                           RoutingNextScore;   // Lower is better (0: perfect score)
1561
    ImGuiID                         RoutingCurr;
1562
    ImGuiID                         RoutingNext;
1563
1564
0
    ImGuiKeyRoutingData()           { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 0; RoutingCurr = RoutingNext = ImGuiKeyOwner_NoOwner; }
1565
};
1566
1567
// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.
1568
// Stored in main context (1 instance)
1569
struct ImGuiKeyRoutingTable
1570
{
1571
    ImGuiKeyRoutingIndex            Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[]
1572
    ImVector<ImGuiKeyRoutingData>   Entries;
1573
    ImVector<ImGuiKeyRoutingData>   EntriesNext;                    // Double-buffer to avoid reallocation (could use a shared buffer)
1574
1575
1
    ImGuiKeyRoutingTable()          { Clear(); }
1576
312
    void Clear()                    { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); }
1577
};
1578
1579
// This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features)
1580
// Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData.
1581
struct ImGuiKeyOwnerData
1582
{
1583
    ImGuiID     OwnerCurr;
1584
    ImGuiID     OwnerNext;
1585
    bool        LockThisFrame;      // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame.
1586
    bool        LockUntilRelease;   // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well.
1587
1588
155
    ImGuiKeyOwnerData()             { OwnerCurr = OwnerNext = ImGuiKeyOwner_NoOwner; LockThisFrame = LockUntilRelease = false; }
1589
};
1590
1591
// Extend ImGuiInputFlags_
1592
// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner()
1593
// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function)
1594
enum ImGuiInputFlagsPrivate_
1595
{
1596
    // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut()
1597
    // - Repeat mode: Repeat rate selection
1598
    ImGuiInputFlags_RepeatRateDefault           = 1 << 1,   // Repeat rate: Regular (default)
1599
    ImGuiInputFlags_RepeatRateNavMove           = 1 << 2,   // Repeat rate: Fast
1600
    ImGuiInputFlags_RepeatRateNavTweak          = 1 << 3,   // Repeat rate: Faster
1601
    // - Repeat mode: Specify when repeating key pressed can be interrupted.
1602
    // - In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in.
1603
    ImGuiInputFlags_RepeatUntilRelease          = 1 << 4,   // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior.
1604
    ImGuiInputFlags_RepeatUntilKeyModsChange    = 1 << 5,   // Stop repeating when released OR if keyboard mods are changed (default for Shortcut)
1605
    ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6,  // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod.
1606
    ImGuiInputFlags_RepeatUntilOtherKeyPress    = 1 << 7,   // Stop repeating when released OR if any other keyboard key is pressed during the repeat
1607
1608
    // Flags for SetKeyOwner(), SetItemKeyOwner()
1609
    // - Locking key away from non-input aware code. Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary.
1610
    ImGuiInputFlags_LockThisFrame               = 1 << 20,  // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame.
1611
    ImGuiInputFlags_LockUntilRelease            = 1 << 21,  // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released.
1612
1613
    // - Condition for SetItemKeyOwner()
1614
    ImGuiInputFlags_CondHovered                 = 1 << 22,  // Only set if item is hovered (default to both)
1615
    ImGuiInputFlags_CondActive                  = 1 << 23,  // Only set if item is active (default to both)
1616
    ImGuiInputFlags_CondDefault_                = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
1617
1618
    // [Internal] Mask of which function support which flags
1619
    ImGuiInputFlags_RepeatRateMask_             = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,
1620
    ImGuiInputFlags_RepeatUntilMask_            = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress,
1621
    ImGuiInputFlags_RepeatMask_                 = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_,
1622
    ImGuiInputFlags_CondMask_                   = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,
1623
    ImGuiInputFlags_RouteTypeMask_              = ImGuiInputFlags_RouteActive | ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteAlways,
1624
    ImGuiInputFlags_RouteOptionsMask_           = ImGuiInputFlags_RouteOverFocused | ImGuiInputFlags_RouteOverActive | ImGuiInputFlags_RouteUnlessBgFocused | ImGuiInputFlags_RouteFromRootWindow,
1625
    ImGuiInputFlags_SupportedByIsKeyPressed     = ImGuiInputFlags_RepeatMask_,
1626
    ImGuiInputFlags_SupportedByIsMouseClicked   = ImGuiInputFlags_Repeat,
1627
    ImGuiInputFlags_SupportedByShortcut         = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_,
1628
    ImGuiInputFlags_SupportedBySetNextItemShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteTypeMask_ | ImGuiInputFlags_RouteOptionsMask_ | ImGuiInputFlags_Tooltip,
1629
    ImGuiInputFlags_SupportedBySetKeyOwner      = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,
1630
    ImGuiInputFlags_SupportedBySetItemKeyOwner  = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,
1631
};
1632
1633
//-----------------------------------------------------------------------------
1634
// [SECTION] Clipper support
1635
//-----------------------------------------------------------------------------
1636
1637
// Note that Max is exclusive, so perhaps should be using a Begin/End convention.
1638
struct ImGuiListClipperRange
1639
{
1640
    int     Min;
1641
    int     Max;
1642
    bool    PosToIndexConvert;      // Begin/End are absolute position (will be converted to indices later)
1643
    ImS8    PosToIndexOffsetMin;    // Add to Min after converting to indices
1644
    ImS8    PosToIndexOffsetMax;    // Add to Min after converting to indices
1645
1646
0
    static ImGuiListClipperRange    FromIndices(int min, int max)                               { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; }
1647
0
    static ImGuiListClipperRange    FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; }
1648
};
1649
1650
// Temporary clipper data, buffers shared/reused between instances
1651
struct ImGuiListClipperData
1652
{
1653
    ImGuiListClipper*               ListClipper;
1654
    float                           LossynessOffset;
1655
    int                             StepNo;
1656
    int                             ItemsFrozen;
1657
    ImVector<ImGuiListClipperRange> Ranges;
1658
1659
0
    ImGuiListClipperData()          { memset(this, 0, sizeof(*this)); }
1660
0
    void                            Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); }
1661
};
1662
1663
//-----------------------------------------------------------------------------
1664
// [SECTION] Navigation support
1665
//-----------------------------------------------------------------------------
1666
1667
enum ImGuiActivateFlags_
1668
{
1669
    ImGuiActivateFlags_None                 = 0,
1670
    ImGuiActivateFlags_PreferInput          = 1 << 0,       // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.
1671
    ImGuiActivateFlags_PreferTweak          = 1 << 1,       // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.
1672
    ImGuiActivateFlags_TryToPreserveState   = 1 << 2,       // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)
1673
    ImGuiActivateFlags_FromTabbing          = 1 << 3,       // Activation requested by a tabbing request (ImGuiNavMoveFlags_IsTabbing)
1674
    ImGuiActivateFlags_FromShortcut         = 1 << 4,       // Activation requested by an item shortcut via SetNextItemShortcut() function.
1675
    ImGuiActivateFlags_FromFocusApi         = 1 << 5,       // Activation requested by an api request (ImGuiNavMoveFlags_FocusApi)
1676
};
1677
1678
// Early work-in-progress API for ScrollToItem()
1679
enum ImGuiScrollFlags_
1680
{
1681
    ImGuiScrollFlags_None                   = 0,
1682
    ImGuiScrollFlags_KeepVisibleEdgeX       = 1 << 0,       // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis]
1683
    ImGuiScrollFlags_KeepVisibleEdgeY       = 1 << 1,       // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]
1684
    ImGuiScrollFlags_KeepVisibleCenterX     = 1 << 2,       // If item is not visible: scroll to make the item centered on X axis [rarely used]
1685
    ImGuiScrollFlags_KeepVisibleCenterY     = 1 << 3,       // If item is not visible: scroll to make the item centered on Y axis
1686
    ImGuiScrollFlags_AlwaysCenterX          = 1 << 4,       // Always center the result item on X axis [rarely used]
1687
    ImGuiScrollFlags_AlwaysCenterY          = 1 << 5,       // Always center the result item on Y axis [default for Y axis for appearing window)
1688
    ImGuiScrollFlags_NoScrollParent         = 1 << 6,       // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to).
1689
    ImGuiScrollFlags_MaskX_                 = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX,
1690
    ImGuiScrollFlags_MaskY_                 = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY,
1691
};
1692
1693
enum ImGuiNavRenderCursorFlags_
1694
{
1695
    ImGuiNavRenderCursorFlags_None          = 0,
1696
    ImGuiNavRenderCursorFlags_Compact       = 1 << 1,       // Compact highlight, no padding/distance from focused item
1697
    ImGuiNavRenderCursorFlags_AlwaysDraw    = 1 << 2,       // Draw rectangular highlight if (g.NavId == id) even when g.NavCursorVisible == false, aka even when using the mouse.
1698
    ImGuiNavRenderCursorFlags_NoRounding    = 1 << 3,
1699
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1700
    ImGuiNavHighlightFlags_None             = ImGuiNavRenderCursorFlags_None,       // Renamed in 1.91.4
1701
    ImGuiNavHighlightFlags_Compact          = ImGuiNavRenderCursorFlags_Compact,    // Renamed in 1.91.4
1702
    ImGuiNavHighlightFlags_AlwaysDraw       = ImGuiNavRenderCursorFlags_AlwaysDraw, // Renamed in 1.91.4
1703
    ImGuiNavHighlightFlags_NoRounding       = ImGuiNavRenderCursorFlags_NoRounding, // Renamed in 1.91.4
1704
    //ImGuiNavHighlightFlags_TypeThin       = ImGuiNavRenderCursorFlags_Compact,    // Renamed in 1.90.2
1705
#endif
1706
};
1707
1708
enum ImGuiNavMoveFlags_
1709
{
1710
    ImGuiNavMoveFlags_None                  = 0,
1711
    ImGuiNavMoveFlags_LoopX                 = 1 << 0,   // On failed request, restart from opposite side
1712
    ImGuiNavMoveFlags_LoopY                 = 1 << 1,
1713
    ImGuiNavMoveFlags_WrapX                 = 1 << 2,   // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)
1714
    ImGuiNavMoveFlags_WrapY                 = 1 << 3,   // This is not super useful but provided for completeness
1715
    ImGuiNavMoveFlags_WrapMask_             = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY,
1716
    ImGuiNavMoveFlags_AllowCurrentNavId     = 1 << 4,   // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)
1717
    ImGuiNavMoveFlags_AlsoScoreVisibleSet   = 1 << 5,   // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)
1718
    ImGuiNavMoveFlags_ScrollToEdgeY         = 1 << 6,   // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary
1719
    ImGuiNavMoveFlags_Forwarded             = 1 << 7,
1720
    ImGuiNavMoveFlags_DebugNoResult         = 1 << 8,   // Dummy scoring for debug purpose, don't apply result
1721
    ImGuiNavMoveFlags_FocusApi              = 1 << 9,   // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details)
1722
    ImGuiNavMoveFlags_IsTabbing             = 1 << 10,  // == Focus + Activate if item is Inputable + DontChangeNavHighlight
1723
    ImGuiNavMoveFlags_IsPageMove            = 1 << 11,  // Identify a PageDown/PageUp request.
1724
    ImGuiNavMoveFlags_Activate              = 1 << 12,  // Activate/select target item.
1725
    ImGuiNavMoveFlags_NoSelect              = 1 << 13,  // Don't trigger selection by not setting g.NavJustMovedTo
1726
    ImGuiNavMoveFlags_NoSetNavCursorVisible = 1 << 14,  // Do not alter the nav cursor visible state
1727
    ImGuiNavMoveFlags_NoClearActiveId       = 1 << 15,  // (Experimental) Do not clear active id when applying move result
1728
};
1729
1730
enum ImGuiNavLayer
1731
{
1732
    ImGuiNavLayer_Main  = 0,    // Main scrolling layer
1733
    ImGuiNavLayer_Menu  = 1,    // Menu layer (access with Alt)
1734
    ImGuiNavLayer_COUNT
1735
};
1736
1737
// Storage for navigation query/results
1738
struct ImGuiNavItemData
1739
{
1740
    ImGuiWindow*        Window;         // Init,Move    // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)
1741
    ImGuiID             ID;             // Init,Move    // Best candidate item ID
1742
    ImGuiID             FocusScopeId;   // Init,Move    // Best candidate focus scope ID
1743
    ImRect              RectRel;        // Init,Move    // Best candidate bounding box in window relative space
1744
    ImGuiItemFlags      ItemFlags;      // ????,Move    // Best candidate item flags
1745
    float               DistBox;        //      Move    // Best candidate box distance to current NavId
1746
    float               DistCenter;     //      Move    // Best candidate center distance to current NavId
1747
    float               DistAxial;      //      Move    // Best candidate axial distance to current NavId
1748
    ImGuiSelectionUserData SelectionUserData;//I+Mov    // Best candidate SetNextItemSelectionUserData() value. Valid if (ItemFlags & ImGuiItemFlags_HasSelectionUserData)
1749
1750
5
    ImGuiNavItemData()  { Clear(); }
1751
5
    void Clear()        { Window = NULL; ID = FocusScopeId = 0; ItemFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }
1752
};
1753
1754
// Storage for PushFocusScope(), g.FocusScopeStack[], g.NavFocusRoute[]
1755
struct ImGuiFocusScopeData
1756
{
1757
    ImGuiID             ID;
1758
    ImGuiID             WindowID;
1759
};
1760
1761
//-----------------------------------------------------------------------------
1762
// [SECTION] Typing-select support
1763
//-----------------------------------------------------------------------------
1764
1765
// Flags for GetTypingSelectRequest()
1766
enum ImGuiTypingSelectFlags_
1767
{
1768
    ImGuiTypingSelectFlags_None                 = 0,
1769
    ImGuiTypingSelectFlags_AllowBackspace       = 1 << 0,   // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state)
1770
    ImGuiTypingSelectFlags_AllowSingleCharMode  = 1 << 1,   // Allow "single char" search mode which is activated when pressing the same character multiple times.
1771
};
1772
1773
// Returned by GetTypingSelectRequest(), designed to eventually be public.
1774
struct IMGUI_API ImGuiTypingSelectRequest
1775
{
1776
    ImGuiTypingSelectFlags  Flags;              // Flags passed to GetTypingSelectRequest()
1777
    int                     SearchBufferLen;
1778
    const char*             SearchBuffer;       // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize).
1779
    bool                    SelectRequest;      // Set when buffer was modified this frame, requesting a selection.
1780
    bool                    SingleCharMode;     // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication.
1781
    ImS8                    SingleCharSize;     // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input.
1782
};
1783
1784
// Storage for GetTypingSelectRequest()
1785
struct IMGUI_API ImGuiTypingSelectState
1786
{
1787
    ImGuiTypingSelectRequest Request;           // User-facing data
1788
    char            SearchBuffer[64];           // Search buffer: no need to make dynamic as this search is very transient.
1789
    ImGuiID         FocusScope;
1790
    int             LastRequestFrame = 0;
1791
    float           LastRequestTime = 0.0f;
1792
    bool            SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing.
1793
1794
1
    ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); }
1795
0
    void            Clear()  { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging
1796
};
1797
1798
//-----------------------------------------------------------------------------
1799
// [SECTION] Columns support
1800
//-----------------------------------------------------------------------------
1801
1802
// Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays!
1803
enum ImGuiOldColumnFlags_
1804
{
1805
    ImGuiOldColumnFlags_None                    = 0,
1806
    ImGuiOldColumnFlags_NoBorder                = 1 << 0,   // Disable column dividers
1807
    ImGuiOldColumnFlags_NoResize                = 1 << 1,   // Disable resizing columns when clicking on the dividers
1808
    ImGuiOldColumnFlags_NoPreserveWidths        = 1 << 2,   // Disable column width preservation when adjusting columns
1809
    ImGuiOldColumnFlags_NoForceWithinWindow     = 1 << 3,   // Disable forcing columns to fit within window
1810
    ImGuiOldColumnFlags_GrowParentContentsSize  = 1 << 4,   // Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.
1811
1812
    // Obsolete names (will be removed)
1813
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1814
    //ImGuiColumnsFlags_None                    = ImGuiOldColumnFlags_None,
1815
    //ImGuiColumnsFlags_NoBorder                = ImGuiOldColumnFlags_NoBorder,
1816
    //ImGuiColumnsFlags_NoResize                = ImGuiOldColumnFlags_NoResize,
1817
    //ImGuiColumnsFlags_NoPreserveWidths        = ImGuiOldColumnFlags_NoPreserveWidths,
1818
    //ImGuiColumnsFlags_NoForceWithinWindow     = ImGuiOldColumnFlags_NoForceWithinWindow,
1819
    //ImGuiColumnsFlags_GrowParentContentsSize  = ImGuiOldColumnFlags_GrowParentContentsSize,
1820
#endif
1821
};
1822
1823
struct ImGuiOldColumnData
1824
{
1825
    float               OffsetNorm;             // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)
1826
    float               OffsetNormBeforeResize;
1827
    ImGuiOldColumnFlags Flags;                  // Not exposed
1828
    ImRect              ClipRect;
1829
1830
0
    ImGuiOldColumnData() { memset(this, 0, sizeof(*this)); }
1831
};
1832
1833
struct ImGuiOldColumns
1834
{
1835
    ImGuiID             ID;
1836
    ImGuiOldColumnFlags Flags;
1837
    bool                IsFirstFrame;
1838
    bool                IsBeingResized;
1839
    int                 Current;
1840
    int                 Count;
1841
    float               OffMinX, OffMaxX;       // Offsets from HostWorkRect.Min.x
1842
    float               LineMinY, LineMaxY;
1843
    float               HostCursorPosY;         // Backup of CursorPos at the time of BeginColumns()
1844
    float               HostCursorMaxPosX;      // Backup of CursorMaxPos at the time of BeginColumns()
1845
    ImRect              HostInitialClipRect;    // Backup of ClipRect at the time of BeginColumns()
1846
    ImRect              HostBackupClipRect;     // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground()
1847
    ImRect              HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns()
1848
    ImVector<ImGuiOldColumnData> Columns;
1849
    ImDrawListSplitter  Splitter;
1850
1851
0
    ImGuiOldColumns()   { memset(this, 0, sizeof(*this)); }
1852
};
1853
1854
//-----------------------------------------------------------------------------
1855
// [SECTION] Box-select support
1856
//-----------------------------------------------------------------------------
1857
1858
struct ImGuiBoxSelectState
1859
{
1860
    // Active box-selection data (persistent, 1 active at a time)
1861
    ImGuiID                 ID;
1862
    bool                    IsActive;
1863
    bool                    IsStarting;
1864
    bool                    IsStartedFromVoid;  // Starting click was not from an item.
1865
    bool                    IsStartedSetNavIdOnce;
1866
    bool                    RequestClear;
1867
    ImGuiKeyChord           KeyMods : 16;       // Latched key-mods for box-select logic.
1868
    ImVec2                  StartPosRel;        // Start position in window-contents relative space (to support scrolling)
1869
    ImVec2                  EndPosRel;          // End position in window-contents relative space
1870
    ImVec2                  ScrollAccum;        // Scrolling accumulator (to behave at high-frame spaces)
1871
    ImGuiWindow*            Window;
1872
1873
    // Temporary/Transient data
1874
    bool                    UnclipMode;         // (Temp/Transient, here in hot area). Set/cleared by the BeginMultiSelect()/EndMultiSelect() owning active box-select.
1875
    ImRect                  UnclipRect;         // Rectangle where ItemAdd() clipping may be temporarily disabled. Need support by multi-select supporting widgets.
1876
    ImRect                  BoxSelectRectPrev;  // Selection rectangle in absolute coordinates (derived every frame from BoxSelectStartPosRel and MousePos)
1877
    ImRect                  BoxSelectRectCurr;
1878
1879
1
    ImGuiBoxSelectState()   { memset(this, 0, sizeof(*this)); }
1880
};
1881
1882
//-----------------------------------------------------------------------------
1883
// [SECTION] Multi-select support
1884
//-----------------------------------------------------------------------------
1885
1886
// We always assume that -1 is an invalid value (which works for indices and pointers)
1887
19
#define ImGuiSelectionUserData_Invalid        ((ImGuiSelectionUserData)-1)
1888
1889
// Temporary storage for multi-select
1890
struct IMGUI_API ImGuiMultiSelectTempData
1891
{
1892
    ImGuiMultiSelectIO      IO;                 // MUST BE FIRST FIELD. Requests are set and returned by BeginMultiSelect()/EndMultiSelect() + written to by user during the loop.
1893
    ImGuiMultiSelectState*  Storage;
1894
    ImGuiID                 FocusScopeId;       // Copied from g.CurrentFocusScopeId (unless another selection scope was pushed manually)
1895
    ImGuiMultiSelectFlags   Flags;
1896
    ImVec2                  ScopeRectMin;
1897
    ImVec2                  BackupCursorMaxPos;
1898
    ImGuiSelectionUserData  LastSubmittedItem;  // Copy of last submitted item data, used to merge output ranges.
1899
    ImGuiID                 BoxSelectId;
1900
    ImGuiKeyChord           KeyMods;
1901
    ImS8                    LoopRequestSetAll;  // -1: no operation, 0: clear all, 1: select all.
1902
    bool                    IsEndIO;            // Set when switching IO from BeginMultiSelect() to EndMultiSelect() state.
1903
    bool                    IsFocused;          // Set if currently focusing the selection scope (any item of the selection). May be used if you have custom shortcut associated to selection.
1904
    bool                    IsKeyboardSetRange; // Set by BeginMultiSelect() when using Shift+Navigation. Because scrolling may be affected we can't afford a frame of lag with Shift+Navigation.
1905
    bool                    NavIdPassedBy;
1906
    bool                    RangeSrcPassedBy;   // Set by the item that matches RangeSrcItem.
1907
    bool                    RangeDstPassedBy;   // Set by the item that matches NavJustMovedToId when IsSetRange is set.
1908
1909
0
    ImGuiMultiSelectTempData()  { Clear(); }
1910
0
    void Clear()            { size_t io_sz = sizeof(IO); ClearIO(); memset((void*)(&IO + 1), 0, sizeof(*this) - io_sz); } // Zero-clear except IO as we preserve IO.Requests[] buffer allocation.
1911
0
    void ClearIO()          { IO.Requests.resize(0); IO.RangeSrcItem = IO.NavIdItem = ImGuiSelectionUserData_Invalid; IO.NavIdSelected = IO.RangeSrcReset = false; }
1912
};
1913
1914
// Persistent storage for multi-select (as long as selection is alive)
1915
struct IMGUI_API ImGuiMultiSelectState
1916
{
1917
    ImGuiWindow*            Window;
1918
    ImGuiID                 ID;
1919
    int                     LastFrameActive;    // Last used frame-count, for GC.
1920
    int                     LastSelectionSize;  // Set by BeginMultiSelect() based on optional info provided by user. May be -1 if unknown.
1921
    ImS8                    RangeSelected;      // -1 (don't have) or true/false
1922
    ImS8                    NavIdSelected;      // -1 (don't have) or true/false
1923
    ImGuiSelectionUserData  RangeSrcItem;       //
1924
    ImGuiSelectionUserData  NavIdItem;          // SetNextItemSelectionUserData() value for NavId (if part of submitted items)
1925
1926
0
    ImGuiMultiSelectState() { Window = NULL; ID = 0; LastFrameActive = LastSelectionSize = 0; RangeSelected = NavIdSelected = -1; RangeSrcItem = NavIdItem = ImGuiSelectionUserData_Invalid; }
1927
};
1928
1929
//-----------------------------------------------------------------------------
1930
// [SECTION] Docking support
1931
//-----------------------------------------------------------------------------
1932
1933
#ifdef IMGUI_HAS_DOCK
1934
// <this is filled in 'docking' branch>
1935
#endif // #ifdef IMGUI_HAS_DOCK
1936
1937
//-----------------------------------------------------------------------------
1938
// [SECTION] Viewport support
1939
//-----------------------------------------------------------------------------
1940
1941
// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!)
1942
// Every instance of ImGuiViewport is in fact a ImGuiViewportP.
1943
struct ImGuiViewportP : public ImGuiViewport
1944
{
1945
    int                 BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used
1946
    ImDrawList*         BgFgDrawLists[2];       // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.
1947
    ImDrawData          DrawDataP;
1948
    ImDrawDataBuilder   DrawDataBuilder;        // Temporary data while building final ImDrawData
1949
1950
    // Per-viewport work area
1951
    // - Insets are >= 0.0f values, distance from viewport corners to work area.
1952
    // - BeginMainMenuBar() and DockspaceOverViewport() tend to use work area to avoid stepping over existing contents.
1953
    // - Generally 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land.
1954
    ImVec2              WorkInsetMin;           // Work Area inset locked for the frame. GetWorkRect() always fits within GetMainRect().
1955
    ImVec2              WorkInsetMax;           // "
1956
    ImVec2              BuildWorkInsetMin;      // Work Area inset accumulator for current frame, to become next frame's WorkInset
1957
    ImVec2              BuildWorkInsetMax;      // "
1958
1959
1
    ImGuiViewportP()    { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; }
1960
1
    ~ImGuiViewportP()   { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); }
1961
1962
    // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)
1963
161k
    ImVec2  CalcWorkRectPos(const ImVec2& inset_min) const                           { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); }
1964
161k
    ImVec2  CalcWorkRectSize(const ImVec2& inset_min, const ImVec2& inset_max) const { return ImVec2(ImMax(0.0f, Size.x - inset_min.x - inset_max.x), ImMax(0.0f, Size.y - inset_min.y - inset_max.y)); }
1965
80.5k
    void    UpdateWorkRect()            { WorkPos = CalcWorkRectPos(WorkInsetMin); WorkSize = CalcWorkRectSize(WorkInsetMin, WorkInsetMax); } // Update public fields
1966
1967
    // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)
1968
243k
    ImRect  GetMainRect() const         { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
1969
161k
    ImRect  GetWorkRect() const         { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }
1970
80.5k
    ImRect  GetBuildWorkRect() const    { ImVec2 pos = CalcWorkRectPos(BuildWorkInsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkInsetMin, BuildWorkInsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }
1971
};
1972
1973
//-----------------------------------------------------------------------------
1974
// [SECTION] Settings support
1975
//-----------------------------------------------------------------------------
1976
1977
// Windows data saved in imgui.ini file
1978
// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily.
1979
// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure)
1980
struct ImGuiWindowSettings
1981
{
1982
    ImGuiID     ID;
1983
    ImVec2ih    Pos;
1984
    ImVec2ih    Size;
1985
    bool        Collapsed;
1986
    bool        IsChild;
1987
    bool        WantApply;      // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
1988
    bool        WantDelete;     // Set to invalidate/delete the settings entry
1989
1990
3
    ImGuiWindowSettings()       { memset(this, 0, sizeof(*this)); }
1991
6
    char* GetName()             { return (char*)(this + 1); }
1992
};
1993
1994
struct ImGuiSettingsHandler
1995
{
1996
    const char* TypeName;       // Short description stored in .ini file. Disallowed characters: '[' ']'
1997
    ImGuiID     TypeHash;       // == ImHashStr(TypeName)
1998
    void        (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Clear all settings data
1999
    void        (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Read: Called before reading (in registration order)
2000
    void*       (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);              // Read: Called when entering into a new ini entry e.g. "[Window][Name]"
2001
    void        (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry
2002
    void        (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Read: Called after reading (in registration order)
2003
    void        (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf);      // Write: Output every entries into 'out_buf'
2004
    void*       UserData;
2005
2006
2
    ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); }
2007
};
2008
2009
//-----------------------------------------------------------------------------
2010
// [SECTION] Localization support
2011
//-----------------------------------------------------------------------------
2012
2013
// This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack.
2014
enum ImGuiLocKey : int
2015
{
2016
    ImGuiLocKey_VersionStr,
2017
    ImGuiLocKey_TableSizeOne,
2018
    ImGuiLocKey_TableSizeAllFit,
2019
    ImGuiLocKey_TableSizeAllDefault,
2020
    ImGuiLocKey_TableResetOrder,
2021
    ImGuiLocKey_WindowingMainMenuBar,
2022
    ImGuiLocKey_WindowingPopup,
2023
    ImGuiLocKey_WindowingUntitled,
2024
    ImGuiLocKey_OpenLink_s,
2025
    ImGuiLocKey_CopyLink,
2026
    ImGuiLocKey_COUNT
2027
};
2028
2029
struct ImGuiLocEntry
2030
{
2031
    ImGuiLocKey     Key;
2032
    const char*     Text;
2033
};
2034
2035
//-----------------------------------------------------------------------------
2036
// [SECTION] Error handling, State recovery support
2037
//-----------------------------------------------------------------------------
2038
2039
// Macros used by Recoverable Error handling
2040
// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro).
2041
// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler.
2042
// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling.
2043
// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling.
2044
#ifndef IM_ASSERT_USER_ERROR
2045
2
#define IM_ASSERT_USER_ERROR(_EXPR,_MSG)    do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0)    // Recoverable User Error
2046
#endif
2047
2048
// The error callback is currently not public, as it is expected that only advanced users will rely on it.
2049
typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback
2050
2051
//-----------------------------------------------------------------------------
2052
// [SECTION] Metrics, Debug Tools
2053
//-----------------------------------------------------------------------------
2054
2055
// See IMGUI_DEBUG_LOG() and IMGUI_DEBUG_LOG_XXX() macros.
2056
enum ImGuiDebugLogFlags_
2057
{
2058
    // Event types
2059
    ImGuiDebugLogFlags_None                 = 0,
2060
    ImGuiDebugLogFlags_EventError           = 1 << 0,   // Error submitted by IM_ASSERT_USER_ERROR()
2061
    ImGuiDebugLogFlags_EventActiveId        = 1 << 1,
2062
    ImGuiDebugLogFlags_EventFocus           = 1 << 2,
2063
    ImGuiDebugLogFlags_EventPopup           = 1 << 3,
2064
    ImGuiDebugLogFlags_EventNav             = 1 << 4,
2065
    ImGuiDebugLogFlags_EventClipper         = 1 << 5,
2066
    ImGuiDebugLogFlags_EventSelection       = 1 << 6,
2067
    ImGuiDebugLogFlags_EventIO              = 1 << 7,
2068
    ImGuiDebugLogFlags_EventFont            = 1 << 8,
2069
    ImGuiDebugLogFlags_EventInputRouting    = 1 << 9,
2070
    ImGuiDebugLogFlags_EventDocking         = 1 << 10,  // Unused in this branch
2071
    ImGuiDebugLogFlags_EventViewport        = 1 << 11,  // Unused in this branch
2072
2073
    ImGuiDebugLogFlags_EventMask_           = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport,
2074
    ImGuiDebugLogFlags_OutputToTTY          = 1 << 20,  // Also send output to TTY
2075
    ImGuiDebugLogFlags_OutputToTestEngine   = 1 << 21,  // Also send output to Test Engine
2076
};
2077
2078
struct ImGuiDebugAllocEntry
2079
{
2080
    int         FrameCount;
2081
    ImS16       AllocCount;
2082
    ImS16       FreeCount;
2083
};
2084
2085
struct ImGuiDebugAllocInfo
2086
{
2087
    int         TotalAllocCount;            // Number of call to MemAlloc().
2088
    int         TotalFreeCount;
2089
    ImS16       LastEntriesIdx;             // Current index in buffer
2090
    ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations
2091
2092
1
    ImGuiDebugAllocInfo() { memset(this, 0, sizeof(*this)); }
2093
};
2094
2095
struct ImGuiMetricsConfig
2096
{
2097
    bool        ShowDebugLog = false;
2098
    bool        ShowIDStackTool = false;
2099
    bool        ShowWindowsRects = false;
2100
    bool        ShowWindowsBeginOrder = false;
2101
    bool        ShowTablesRects = false;
2102
    bool        ShowDrawCmdMesh = true;
2103
    bool        ShowDrawCmdBoundingBoxes = true;
2104
    bool        ShowTextEncodingViewer = false;
2105
    bool        ShowTextureUsedRect = false;
2106
    int         ShowWindowsRectsType = -1;
2107
    int         ShowTablesRectsType = -1;
2108
    int         HighlightMonitorIdx = -1;
2109
    ImGuiID     HighlightViewportID = 0;
2110
    bool        ShowFontPreview = true;
2111
};
2112
2113
struct ImGuiStackLevelInfo
2114
{
2115
    ImGuiID                 ID;
2116
    ImS8                    QueryFrameCount;            // >= 1: Query in progress
2117
    bool                    QuerySuccess;               // Obtained result from DebugHookIdInfo()
2118
    ImS8                    DataType;                   // ImGuiDataType
2119
    int                     DescOffset;                 // -1 or offset into parent's ResultPathsBuf
2120
2121
0
    ImGuiStackLevelInfo()   { memset(this, 0, sizeof(*this)); DescOffset = -1; }
2122
};
2123
2124
// State for ID Stack tool queries
2125
struct ImGuiIDStackTool
2126
{
2127
    int                     LastActiveFrame;
2128
    int                     StackLevel;                 // -1: query stack and resize Results, >= 0: individual stack level
2129
    ImGuiID                 QueryMainId;                // ID to query details for
2130
    ImVector<ImGuiStackLevelInfo> Results;
2131
    bool                    QueryHookActive;            // Used to disambiguate the case where DebugHookIdInfoId == 0 which is valid.
2132
    bool                    OptHexEncodeNonAsciiChars;
2133
    bool                    OptCopyToClipboardOnCtrlC;
2134
    float                   CopyToClipboardLastTime;
2135
    ImGuiTextBuffer         ResultPathsBuf;
2136
    ImGuiTextBuffer         ResultTempBuf;
2137
2138
1
    ImGuiIDStackTool()      { memset(this, 0, sizeof(*this)); LastActiveFrame = -1; OptHexEncodeNonAsciiChars = true; CopyToClipboardLastTime = -FLT_MAX; }
2139
};
2140
2141
//-----------------------------------------------------------------------------
2142
// [SECTION] Generic context hooks
2143
//-----------------------------------------------------------------------------
2144
2145
typedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook);
2146
enum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ };
2147
2148
struct ImGuiContextHook
2149
{
2150
    ImGuiID                     HookId;     // A unique ID assigned by AddContextHook()
2151
    ImGuiContextHookType        Type;
2152
    ImGuiID                     Owner;
2153
    ImGuiContextHookCallback    Callback;
2154
    void*                       UserData;
2155
2156
0
    ImGuiContextHook()          { memset(this, 0, sizeof(*this)); }
2157
};
2158
2159
//-----------------------------------------------------------------------------
2160
// [SECTION] ImGuiContext (main Dear ImGui context)
2161
//-----------------------------------------------------------------------------
2162
2163
struct ImGuiContext
2164
{
2165
    bool                    Initialized;
2166
    ImGuiIO                 IO;
2167
    ImGuiPlatformIO         PlatformIO;
2168
    ImGuiStyle              Style;
2169
    ImVector<ImFontAtlas*>  FontAtlases;                        // List of font atlases used by the context (generally only contains g.IO.Fonts aka the main font atlas)
2170
    ImFont*                 Font;                               // Currently bound font. (== FontStack.back().Font)
2171
    ImFontBaked*            FontBaked;                          // Currently bound font at currently bound size. (== Font->GetFontBaked(FontSize))
2172
    float                   FontSize;                           // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function).
2173
    float                   FontSizeBase;                       // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified.
2174
    float                   FontBakedScale;                     // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f.
2175
    float                   FontRasterizerDensity;              // Current font density. Used by all calls to GetFontBaked().
2176
    float                   CurrentDpiScale;                    // Current window/viewport DpiScale == CurrentViewport->DpiScale
2177
    ImDrawListSharedData    DrawListSharedData;
2178
    double                  Time;
2179
    int                     FrameCount;
2180
    int                     FrameCountEnded;
2181
    int                     FrameCountRendered;
2182
    ImGuiID                 WithinEndChildID;                   // Set within EndChild()
2183
    bool                    WithinFrameScope;                   // Set by NewFrame(), cleared by EndFrame()
2184
    bool                    WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed
2185
    bool                    GcCompactAll;                       // Request full GC
2186
    bool                    TestEngineHookItems;                // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()
2187
    void*                   TestEngine;                         // Test engine user data
2188
    char                    ContextName[16];                    // Storage for a context name (to facilitate debugging multi-context setups)
2189
2190
    // Inputs
2191
    ImVector<ImGuiInputEvent> InputEventsQueue;                 // Input events which will be trickled/written into IO structure.
2192
    ImVector<ImGuiInputEvent> InputEventsTrail;                 // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.
2193
    ImGuiMouseSource        InputEventsNextMouseSource;
2194
    ImU32                   InputEventsNextEventId;
2195
2196
    // Windows state
2197
    ImVector<ImGuiWindow*>  Windows;                            // Windows, sorted in display order, back to front
2198
    ImVector<ImGuiWindow*>  WindowsFocusOrder;                  // Root windows, sorted in focus order, back to front.
2199
    ImVector<ImGuiWindow*>  WindowsTempSortBuffer;              // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child
2200
    ImVector<ImGuiWindowStackData> CurrentWindowStack;
2201
    ImGuiStorage            WindowsById;                        // Map window's ImGuiID to ImGuiWindow*
2202
    int                     WindowsActiveCount;                 // Number of unique windows submitted by frame
2203
    float                   WindowsBorderHoverPadding;          // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, style.WindowBorderHoverPadding). This isn't so multi-dpi friendly.
2204
    ImGuiID                 DebugBreakInWindow;                 // Set to break in Begin() call.
2205
    ImGuiWindow*            CurrentWindow;                      // Window being drawn into
2206
    ImGuiWindow*            HoveredWindow;                      // Window the mouse is hovering. Will typically catch mouse inputs.
2207
    ImGuiWindow*            HoveredWindowUnderMovingWindow;     // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
2208
    ImGuiWindow*            HoveredWindowBeforeClear;           // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors.
2209
    ImGuiWindow*            MovingWindow;                       // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.
2210
    ImGuiWindow*            WheelingWindow;                     // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.
2211
    ImVec2                  WheelingWindowRefMousePos;
2212
    int                     WheelingWindowStartFrame;           // This may be set one frame before WheelingWindow is != NULL
2213
    int                     WheelingWindowScrolledFrame;
2214
    float                   WheelingWindowReleaseTimer;
2215
    ImVec2                  WheelingWindowWheelRemainder;
2216
    ImVec2                  WheelingAxisAvg;
2217
2218
    // Item/widgets state and tracking information
2219
    ImGuiID                 DebugDrawIdConflictsId;             // Set when we detect multiple items with the same identifier
2220
    ImGuiID                 DebugHookIdInfoId;                  // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]
2221
    ImGuiID                 HoveredId;                          // Hovered widget, filled during the frame
2222
    ImGuiID                 HoveredIdPreviousFrame;
2223
    int                     HoveredIdPreviousFrameItemCount;    // Count numbers of items using the same ID as last frame's hovered id
2224
    float                   HoveredIdTimer;                     // Measure contiguous hovering time
2225
    float                   HoveredIdNotActiveTimer;            // Measure contiguous hovering time where the item has not been active
2226
    bool                    HoveredIdAllowOverlap;
2227
    bool                    HoveredIdIsDisabled;                // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.
2228
    bool                    ItemUnclipByLog;                    // Disable ItemAdd() clipping, essentially a memory-locality friendly copy of LogEnabled
2229
    ImGuiID                 ActiveId;                           // Active widget
2230
    ImGuiID                 ActiveIdIsAlive;                    // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)
2231
    float                   ActiveIdTimer;
2232
    bool                    ActiveIdIsJustActivated;            // Set at the time of activation for one frame
2233
    bool                    ActiveIdAllowOverlap;               // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)
2234
    bool                    ActiveIdNoClearOnFocusLoss;         // Disable losing active id if the active id window gets unfocused.
2235
    bool                    ActiveIdHasBeenPressedBefore;       // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.
2236
    bool                    ActiveIdHasBeenEditedBefore;        // Was the value associated to the widget Edited over the course of the Active state.
2237
    bool                    ActiveIdHasBeenEditedThisFrame;
2238
    bool                    ActiveIdFromShortcut;
2239
    ImGuiID                 ActiveIdDisabledId;                 // When clicking a disabled item we set ActiveId=window->MoveId to avoid interference with widget code. Actual item ID is stored here.
2240
    int                     ActiveIdMouseButton : 8;
2241
    ImVec2                  ActiveIdClickOffset;                // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)
2242
    ImGuiWindow*            ActiveIdWindow;
2243
    ImGuiInputSource        ActiveIdSource;                     // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad
2244
    ImGuiID                 ActiveIdPreviousFrame;
2245
    ImGuiDeactivatedItemData DeactivatedItemData;
2246
    ImGuiDataTypeStorage    ActiveIdValueOnActivation;          // Backup of initial value at the time of activation. ONLY SET BY SPECIFIC WIDGETS: DragXXX and SliderXXX.
2247
    ImGuiID                 LastActiveId;                       // Store the last non-zero ActiveId, useful for animation.
2248
    float                   LastActiveIdTimer;                  // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.
2249
2250
    // Key/Input Ownership + Shortcut Routing system
2251
    // - The idea is that instead of "eating" a given key, we can link to an owner.
2252
    // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.
2253
    // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().
2254
    double                  LastKeyModsChangeTime;              // Record the last time key mods changed (affect repeat delay when using shortcut logic)
2255
    double                  LastKeyModsChangeFromNoneTime;      // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic)
2256
    double                  LastKeyboardKeyPressTime;           // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed.
2257
    ImBitArrayForNamedKeys  KeysMayBeCharInput;                 // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes
2258
    ImGuiKeyOwnerData       KeysOwnerData[ImGuiKey_NamedKey_COUNT];
2259
    ImGuiKeyRoutingTable    KeysRoutingTable;
2260
    ImU32                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
2261
    bool                    ActiveIdUsingAllKeyboardKeys;       // Active widget will want to read all keyboard keys inputs. (this is a shortcut for not taking ownership of 100+ keys, frequently used by drag operations)
2262
    ImGuiKeyChord           DebugBreakInShortcutRouting;        // Set to break in SetShortcutRouting()/Shortcut() calls.
2263
    //ImU32                 ActiveIdUsingNavInputMask;          // [OBSOLETE] Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes --> 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
2264
2265
    // Next window/item data
2266
    ImGuiID                 CurrentFocusScopeId;                // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId.
2267
    ImGuiItemFlags          CurrentItemFlags;                   // Value for currently appending items == g.ItemFlagsStack.back()
2268
    ImGuiID                 DebugLocateId;                      // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location
2269
    ImGuiNextItemData       NextItemData;                       // Storage for SetNextItem** functions
2270
    ImGuiLastItemData       LastItemData;                       // Storage for last submitted item (setup by ItemAdd)
2271
    ImGuiNextWindowData     NextWindowData;                     // Storage for SetNextWindow** functions
2272
    bool                    DebugShowGroupRects;
2273
2274
    // Shared stacks
2275
    ImGuiCol                        DebugFlashStyleColorIdx;    // (Keep close to ColorStack to share cache line)
2276
    ImVector<ImGuiColorMod>         ColorStack;                 // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()
2277
    ImVector<ImGuiStyleMod>         StyleVarStack;              // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()
2278
    ImVector<ImFontStackData>       FontStack;                  // Stack for PushFont()/PopFont() - inherited by Begin()
2279
    ImVector<ImGuiFocusScopeData>   FocusScopeStack;            // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()
2280
    ImVector<ImGuiItemFlags>        ItemFlagsStack;             // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()
2281
    ImVector<ImGuiGroupData>        GroupStack;                 // Stack for BeginGroup()/EndGroup() - not inherited by Begin()
2282
    ImVector<ImGuiPopupData>        OpenPopupStack;             // Which popups are open (persistent)
2283
    ImVector<ImGuiPopupData>        BeginPopupStack;            // Which level of BeginPopup() we are in (reset every frame)
2284
    ImVector<ImGuiTreeNodeStackData>TreeNodeStack;              // Stack for TreeNode()
2285
2286
    // Viewports
2287
    ImVector<ImGuiViewportP*> Viewports;                        // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.
2288
2289
    // Keyboard/Gamepad Navigation
2290
    bool                    NavCursorVisible;                   // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move.
2291
    bool                    NavHighlightItemUnderNav;           // Disable mouse hovering highlight. Highlight navigation focused item instead of mouse hovered item.
2292
    //bool                  NavDisableHighlight;                // Old name for !g.NavCursorVisible before 1.91.4 (2024/10/18). OPPOSITE VALUE (g.NavDisableHighlight == !g.NavCursorVisible)
2293
    //bool                  NavDisableMouseHover;               // Old name for g.NavHighlightItemUnderNav before 1.91.1 (2024/10/18) this was called When user starts using keyboard/gamepad, we hide mouse hovering highlight until mouse is touched again.
2294
    bool                    NavMousePosDirty;                   // When set we will update mouse position if io.ConfigNavMoveSetMousePos is set (not enabled by default)
2295
    bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid
2296
    ImGuiID                 NavId;                              // Focused item for navigation
2297
    ImGuiWindow*            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'
2298
    ImGuiID                 NavFocusScopeId;                    // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope)
2299
    ImGuiNavLayer           NavLayer;                           // Focused layer (main scrolling layer, or menu/title bar layer)
2300
    ImGuiID                 NavActivateId;                      // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItemByID()
2301
    ImGuiID                 NavActivateDownId;                  // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0
2302
    ImGuiID                 NavActivatePressedId;               // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)
2303
    ImGuiActivateFlags      NavActivateFlags;
2304
    ImVector<ImGuiFocusScopeData> NavFocusRoute;                // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain.
2305
    ImGuiID                 NavHighlightActivatedId;
2306
    float                   NavHighlightActivatedTimer;
2307
    ImGuiID                 NavNextActivateId;                  // Set by ActivateItemByID(), queued until next frame.
2308
    ImGuiActivateFlags      NavNextActivateFlags;
2309
    ImGuiInputSource        NavInputSource;                     // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Gamepad
2310
    ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.
2311
    ImS8                    NavCursorHideFrames;
2312
    //ImGuiID               NavActivateInputId;                 // Removed in 1.89.4 (July 2023). This is now part of g.NavActivateId and sets g.NavActivateFlags |= ImGuiActivateFlags_PreferInput. See commit c9a53aa74, issue #5606.
2313
2314
    // Navigation: Init & Move Requests
2315
    bool                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()
2316
    bool                    NavInitRequest;                     // Init request for appearing window to select first item
2317
    bool                    NavInitRequestFromMove;
2318
    ImGuiNavItemData        NavInitResult;                      // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)
2319
    bool                    NavMoveSubmitted;                   // Move request submitted, will process result on next NewFrame()
2320
    bool                    NavMoveScoringItems;                // Move request submitted, still scoring incoming items
2321
    bool                    NavMoveForwardToNextFrame;
2322
    ImGuiNavMoveFlags       NavMoveFlags;
2323
    ImGuiScrollFlags        NavMoveScrollFlags;
2324
    ImGuiKeyChord           NavMoveKeyMods;
2325
    ImGuiDir                NavMoveDir;                         // Direction of the move request (left/right/up/down)
2326
    ImGuiDir                NavMoveDirForDebug;
2327
    ImGuiDir                NavMoveClipDir;                     // FIXME-NAV: Describe the purpose of this better. Might want to rename?
2328
    ImRect                  NavScoringRect;                     // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.
2329
    ImRect                  NavScoringNoClipRect;               // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted
2330
    int                     NavScoringDebugCount;               // Metrics for debugging
2331
    int                     NavTabbingDir;                      // Generally -1 or +1, 0 when tabbing without a nav id
2332
    int                     NavTabbingCounter;                  // >0 when counting items for tabbing
2333
    ImGuiNavItemData        NavMoveResultLocal;                 // Best move request candidate within NavWindow
2334
    ImGuiNavItemData        NavMoveResultLocalVisible;          // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)
2335
    ImGuiNavItemData        NavMoveResultOther;                 // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)
2336
    ImGuiNavItemData        NavTabbingResultFirst;              // First tabbing request candidate within NavWindow and flattened hierarchy
2337
2338
    // Navigation: record of last move request
2339
    ImGuiID                 NavJustMovedFromFocusScopeId;       // Just navigated from this focus scope id (result of a successfully MoveRequest).
2340
    ImGuiID                 NavJustMovedToId;                   // Just navigated to this id (result of a successfully MoveRequest).
2341
    ImGuiID                 NavJustMovedToFocusScopeId;         // Just navigated to this focus scope id (result of a successfully MoveRequest).
2342
    ImGuiKeyChord           NavJustMovedToKeyMods;
2343
    bool                    NavJustMovedToIsTabbing;            // Copy of ImGuiNavMoveFlags_IsTabbing. Maybe we should store whole flags.
2344
    bool                    NavJustMovedToHasSelectionData;     // Copy of move result's ItemFlags & ImGuiItemFlags_HasSelectionUserData). Maybe we should just store ImGuiNavItemData.
2345
2346
    // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)
2347
    bool                    ConfigNavWindowingWithGamepad;      // = true. Enable CTRL+TAB by holding ImGuiKey_GamepadFaceLeft (== ImGuiKey_NavGamepadMenu). When false, the button may still be used to toggle Menu layer.
2348
    ImGuiKeyChord           ConfigNavWindowingKeyNext;          // = ImGuiMod_Ctrl | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiKey_Tab on OS X). For reconfiguration (see #4828)
2349
    ImGuiKeyChord           ConfigNavWindowingKeyPrev;          // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab (or ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab on OS X)
2350
    ImGuiWindow*            NavWindowingTarget;                 // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!
2351
    ImGuiWindow*            NavWindowingTargetAnim;             // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.
2352
    ImGuiWindow*            NavWindowingListWindow;             // Internal window actually listing the CTRL+Tab contents
2353
    float                   NavWindowingTimer;
2354
    float                   NavWindowingHighlightAlpha;
2355
    ImGuiInputSource        NavWindowingInputSource;
2356
    bool                    NavWindowingToggleLayer;            // Set while Alt or GamepadMenu is held, may be cleared by other operations, and processed when releasing the key.
2357
    ImGuiKey                NavWindowingToggleKey;              // Keyboard/gamepad key used when toggling to menu layer.
2358
    ImVec2                  NavWindowingAccumDeltaPos;
2359
    ImVec2                  NavWindowingAccumDeltaSize;
2360
2361
    // Render
2362
    float                   DimBgRatio;                         // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)
2363
2364
    // Drag and Drop
2365
    bool                    DragDropActive;
2366
    bool                    DragDropWithinSource;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source.
2367
    bool                    DragDropWithinTarget;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target.
2368
    ImGuiDragDropFlags      DragDropSourceFlags;
2369
    int                     DragDropSourceFrameCount;
2370
    int                     DragDropMouseButton;
2371
    ImGuiPayload            DragDropPayload;
2372
    ImRect                  DragDropTargetRect;                 // Store rectangle of current target candidate (we favor small targets when overlapping)
2373
    ImRect                  DragDropTargetClipRect;             // Store ClipRect at the time of item's drawing
2374
    ImGuiID                 DragDropTargetId;
2375
    ImGuiID                 DragDropTargetFullViewport;
2376
    ImGuiDragDropFlags      DragDropAcceptFlags;
2377
    float                   DragDropAcceptIdCurrRectSurface;    // Target item surface (we resolve overlapping targets by prioritizing the smaller surface)
2378
    ImGuiID                 DragDropAcceptIdCurr;               // Target item id (set at the time of accepting the payload)
2379
    ImGuiID                 DragDropAcceptIdPrev;               // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets)
2380
    int                     DragDropAcceptFrameCount;           // Last time a target expressed a desire to accept the source
2381
    ImGuiID                 DragDropHoldJustPressedId;          // Set when holding a payload just made ButtonBehavior() return a press.
2382
    ImVector<unsigned char> DragDropPayloadBufHeap;             // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size
2383
    unsigned char           DragDropPayloadBufLocal[16];        // Local buffer for small payloads
2384
2385
    // Clipper
2386
    int                             ClipperTempDataStacked;
2387
    ImVector<ImGuiListClipperData>  ClipperTempData;
2388
2389
    // Tables
2390
    ImGuiTable*                     CurrentTable;
2391
    ImGuiID                         DebugBreakInTable;          // Set to break in BeginTable() call.
2392
    int                             TablesTempDataStacked;      // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
2393
    ImVector<ImGuiTableTempData>    TablesTempData;             // Temporary table data (buffers reused/shared across instances, support nesting)
2394
    ImPool<ImGuiTable>              Tables;                     // Persistent table data
2395
    ImVector<float>                 TablesLastTimeActive;       // Last used timestamp of each tables (SOA, for efficient GC)
2396
    ImVector<ImDrawChannel>         DrawChannelsTempMergeBuffer;
2397
2398
    // Tab bars
2399
    ImGuiTabBar*                    CurrentTabBar;
2400
    ImPool<ImGuiTabBar>             TabBars;
2401
    ImVector<ImGuiPtrOrIndex>       CurrentTabBarStack;
2402
    ImVector<ImGuiShrinkWidthItem>  ShrinkWidthBuffer;
2403
2404
    // Multi-Select state
2405
    ImGuiBoxSelectState             BoxSelectState;
2406
    ImGuiMultiSelectTempData*       CurrentMultiSelect;
2407
    int                             MultiSelectTempDataStacked; // Temporary multi-select data size (because we leave previous instances undestructed, we generally don't use MultiSelectTempData.Size)
2408
    ImVector<ImGuiMultiSelectTempData> MultiSelectTempData;
2409
    ImPool<ImGuiMultiSelectState>   MultiSelectStorage;
2410
2411
    // Hover Delay system
2412
    ImGuiID                 HoverItemDelayId;
2413
    ImGuiID                 HoverItemDelayIdPreviousFrame;
2414
    float                   HoverItemDelayTimer;                // Currently used by IsItemHovered()
2415
    float                   HoverItemDelayClearTimer;           // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.
2416
    ImGuiID                 HoverItemUnlockedStationaryId;      // Mouse has once been stationary on this item. Only reset after departing the item.
2417
    ImGuiID                 HoverWindowUnlockedStationaryId;    // Mouse has once been stationary on this window. Only reset after departing the window.
2418
2419
    // Mouse state
2420
    ImGuiMouseCursor        MouseCursor;
2421
    float                   MouseStationaryTimer;               // Time the mouse has been stationary (with some loose heuristic)
2422
    ImVec2                  MouseLastValidPos;
2423
2424
    // Widget state
2425
    ImGuiInputTextState     InputTextState;
2426
    ImGuiTextIndex          InputTextLineIndex;                 // Temporary storage
2427
    ImGuiInputTextDeactivatedState InputTextDeactivatedState;
2428
    ImFontBaked             InputTextPasswordFontBackupBaked;
2429
    ImFontFlags             InputTextPasswordFontBackupFlags;
2430
    ImGuiID                 TempInputId;                        // Temporary text input when CTRL+clicking on a slider, etc.
2431
    ImGuiDataTypeStorage    DataTypeZeroValue;                  // 0 for all data types
2432
    int                     BeginMenuDepth;
2433
    int                     BeginComboDepth;
2434
    ImGuiColorEditFlags     ColorEditOptions;                   // Store user options for color edit widgets
2435
    ImGuiID                 ColorEditCurrentID;                 // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others).
2436
    ImGuiID                 ColorEditSavedID;                   // ID we are saving/restoring HS for
2437
    float                   ColorEditSavedHue;                  // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips
2438
    float                   ColorEditSavedSat;                  // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips
2439
    ImU32                   ColorEditSavedColor;                // RGB value with alpha set to 0.
2440
    ImVec4                  ColorPickerRef;                     // Initial/reference color at the time of opening the color picker.
2441
    ImGuiComboPreviewData   ComboPreviewData;
2442
    ImRect                  WindowResizeBorderExpectedRect;     // Expected border rect, switch to relative edit if moving
2443
    bool                    WindowResizeRelativeMode;
2444
    short                   ScrollbarSeekMode;                  // 0: scroll to clicked location, -1/+1: prev/next page.
2445
    float                   ScrollbarClickDeltaToGrabCenter;    // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space.
2446
    float                   SliderGrabClickOffset;
2447
    float                   SliderCurrentAccum;                 // Accumulated slider delta when using navigation controls.
2448
    bool                    SliderCurrentAccumDirty;            // Has the accumulated slider delta changed since last time we tried to apply it?
2449
    bool                    DragCurrentAccumDirty;
2450
    float                   DragCurrentAccum;                   // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings
2451
    float                   DragSpeedDefaultRatio;              // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio
2452
    float                   DisabledAlphaBackup;                // Backup for style.Alpha for BeginDisabled()
2453
    short                   DisabledStackSize;
2454
    short                   TooltipOverrideCount;
2455
    ImGuiWindow*            TooltipPreviousWindow;              // Window of last tooltip submitted during the frame
2456
    ImVector<char>          ClipboardHandlerData;               // If no custom clipboard handler is defined
2457
    ImVector<ImGuiID>       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once
2458
    ImGuiTypingSelectState  TypingSelectState;                  // State for GetTypingSelectRequest()
2459
2460
    // Platform support
2461
    ImGuiPlatformImeData    PlatformImeData;                    // Data updated by current frame. Will be applied at end of the frame. For some backends, this is required to have WantVisible=true in order to receive text message.
2462
    ImGuiPlatformImeData    PlatformImeDataPrev;                // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler.
2463
2464
    // Extensions
2465
    // FIXME: We could provide an API to register one slot in an array held in ImGuiContext?
2466
    ImVector<ImTextureData*> UserTextures;                      // List of textures created/managed by user or third-party extension. Automatically appended into platform_io.Textures[].
2467
2468
    // Settings
2469
    bool                    SettingsLoaded;
2470
    float                   SettingsDirtyTimer;                 // Save .ini Settings to memory when time reaches zero
2471
    ImGuiTextBuffer         SettingsIniData;                    // In memory .ini settings
2472
    ImVector<ImGuiSettingsHandler>      SettingsHandlers;       // List of .ini settings handlers
2473
    ImChunkStream<ImGuiWindowSettings>  SettingsWindows;        // ImGuiWindow .ini settings entries
2474
    ImChunkStream<ImGuiTableSettings>   SettingsTables;         // ImGuiTable .ini settings entries
2475
    ImVector<ImGuiContextHook>          Hooks;                  // Hooks for extensions (e.g. test engine)
2476
    ImGuiID                             HookIdNext;             // Next available HookId
2477
2478
    // Localization
2479
    const char*             LocalizationTable[ImGuiLocKey_COUNT];
2480
2481
    // Capture/Logging
2482
    bool                    LogEnabled;                         // Currently capturing
2483
    ImGuiLogFlags           LogFlags;                           // Capture flags/type
2484
    ImGuiWindow*            LogWindow;
2485
    ImFileHandle            LogFile;                            // If != NULL log to stdout/ file
2486
    ImGuiTextBuffer         LogBuffer;                          // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.
2487
    const char*             LogNextPrefix;                      // See comment in LogSetNextTextDecoration(): doesn't copy underlying data, use carefully!
2488
    const char*             LogNextSuffix;
2489
    float                   LogLinePosY;
2490
    bool                    LogLineFirstItem;
2491
    int                     LogDepthRef;
2492
    int                     LogDepthToExpand;
2493
    int                     LogDepthToExpandDefault;            // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.
2494
2495
    // Error Handling
2496
    ImGuiErrorCallback      ErrorCallback;                      // = NULL. May be exposed in public API eventually.
2497
    void*                   ErrorCallbackUserData;              // = NULL
2498
    ImVec2                  ErrorTooltipLockedPos;
2499
    bool                    ErrorFirst;
2500
    int                     ErrorCountCurrentFrame;             // [Internal] Number of errors submitted this frame.
2501
    ImGuiErrorRecoveryState StackSizesInNewFrame;               // [Internal]
2502
    ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow;  // [Internal]
2503
2504
    // Debug Tools
2505
    // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.)
2506
    int                     DebugDrawIdConflictsCount;          // Locked count (preserved when holding CTRL)
2507
    ImGuiDebugLogFlags      DebugLogFlags;
2508
    ImGuiTextBuffer         DebugLogBuf;
2509
    ImGuiTextIndex          DebugLogIndex;
2510
    int                     DebugLogSkippedErrors;
2511
    ImGuiDebugLogFlags      DebugLogAutoDisableFlags;
2512
    ImU8                    DebugLogAutoDisableFrames;
2513
    ImU8                    DebugLocateFrames;                  // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
2514
    bool                    DebugBreakInLocateId;               // Debug break in ItemAdd() call for g.DebugLocateId.
2515
    ImGuiKeyChord           DebugBreakKeyChord;                 // = ImGuiKey_Pause
2516
    ImS8                    DebugBeginReturnValueCullDepth;     // Cycle between 0..9 then wrap around.
2517
    bool                    DebugItemPickerActive;              // Item picker is active (started with DebugStartItemPicker())
2518
    ImU8                    DebugItemPickerMouseButton;
2519
    ImGuiID                 DebugItemPickerBreakId;             // Will call IM_DEBUG_BREAK() when encountering this ID
2520
    float                   DebugFlashStyleColorTime;
2521
    ImVec4                  DebugFlashStyleColorBackup;
2522
    ImGuiMetricsConfig      DebugMetricsConfig;
2523
    ImGuiIDStackTool        DebugIDStackTool;
2524
    ImGuiDebugAllocInfo     DebugAllocInfo;
2525
#if defined(IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)
2526
    ImGuiStorage            DebugDrawIdConflictsAliveCount;
2527
    ImGuiStorage            DebugDrawIdConflictsHighlightSet;
2528
#endif
2529
2530
    // Misc
2531
    float                   FramerateSecPerFrame[60];           // Calculate estimate of framerate for user over the last 60 frames..
2532
    int                     FramerateSecPerFrameIdx;
2533
    int                     FramerateSecPerFrameCount;
2534
    float                   FramerateSecPerFrameAccum;
2535
    int                     WantCaptureMouseNextFrame;          // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.
2536
    int                     WantCaptureKeyboardNextFrame;       // "
2537
    int                     WantTextInputNextFrame;             // Copied in EndFrame() from g.PlatformImeData.WantTextInput. Needs to be set for some backends (SDL3) to emit character inputs.
2538
    ImVector<char>          TempBuffer;                         // Temporary text buffer
2539
    char                    TempKeychordName[64];
2540
2541
    ImGuiContext(ImFontAtlas* shared_font_atlas);
2542
};
2543
2544
//-----------------------------------------------------------------------------
2545
// [SECTION] ImGuiWindowTempData, ImGuiWindow
2546
//-----------------------------------------------------------------------------
2547
2548
// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.
2549
// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..)
2550
// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin)
2551
struct IMGUI_API ImGuiWindowTempData
2552
{
2553
    // Layout
2554
    ImVec2                  CursorPos;              // Current emitting position, in absolute coordinates.
2555
    ImVec2                  CursorPosPrevLine;
2556
    ImVec2                  CursorStartPos;         // Initial position after Begin(), generally ~ window position + WindowPadding.
2557
    ImVec2                  CursorMaxPos;           // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame.
2558
    ImVec2                  IdealMaxPos;            // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame.
2559
    ImVec2                  CurrLineSize;
2560
    ImVec2                  PrevLineSize;
2561
    float                   CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added).
2562
    float                   PrevLineTextBaseOffset;
2563
    bool                    IsSameLine;
2564
    bool                    IsSetPos;
2565
    ImVec1                  Indent;                 // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)
2566
    ImVec1                  ColumnsOffset;          // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API.
2567
    ImVec1                  GroupOffset;
2568
    ImVec2                  CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area.
2569
2570
    // Keyboard/Gamepad navigation
2571
    ImGuiNavLayer           NavLayerCurrent;        // Current layer, 0..31 (we currently only use 0..1)
2572
    short                   NavLayersActiveMask;    // Which layers have been written to (result from previous frame)
2573
    short                   NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame)
2574
    bool                    NavIsScrollPushableX;   // Set when current work location may be scrolled horizontally when moving left / right. This is generally always true UNLESS within a column.
2575
    bool                    NavHideHighlightOneFrame;
2576
    bool                    NavWindowHasScrollY;    // Set per window when scrolling can be used (== ScrollMax.y > 0.0f)
2577
2578
    // Miscellaneous
2579
    bool                    MenuBarAppending;       // FIXME: Remove this
2580
    ImVec2                  MenuBarOffset;          // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.
2581
    ImGuiMenuColumns        MenuColumns;            // Simplified columns storage for menu items measurement
2582
    int                     TreeDepth;              // Current tree depth.
2583
    ImU32                   TreeHasStackDataDepthMask;      // Store whether given depth has ImGuiTreeNodeStackData data. Could be turned into a ImU64 if necessary.
2584
    ImU32                   TreeRecordsClippedNodesY2Mask;  // Store whether we should keep recording Y2. Cleared when passing clip max. Equivalent TreeHasStackDataDepthMask value should always be set.
2585
    ImVector<ImGuiWindow*>  ChildWindows;
2586
    ImGuiStorage*           StateStorage;           // Current persistent per-window storage (store e.g. tree node open/close state)
2587
    ImGuiOldColumns*        CurrentColumns;         // Current columns set
2588
    int                     CurrentTableIdx;        // Current table index (into g.Tables)
2589
    ImGuiLayoutType         LayoutType;
2590
    ImGuiLayoutType         ParentLayoutType;       // Layout type of parent window at the time of Begin()
2591
    ImU32                   ModalDimBgColor;
2592
    ImGuiItemStatusFlags    WindowItemStatusFlags;
2593
    ImGuiItemStatusFlags    ChildItemStatusFlags;
2594
2595
    // Local parameters stacks
2596
    // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.
2597
    float                   ItemWidth;              // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window).
2598
    float                   TextWrapPos;            // Current text wrap pos.
2599
    ImVector<float>         ItemWidthStack;         // Store item widths to restore (attention: .back() is not == ItemWidth)
2600
    ImVector<float>         TextWrapPosStack;       // Store text wrap pos to restore (attention: .back() is not == TextWrapPos)
2601
};
2602
2603
// Storage for one window
2604
struct IMGUI_API ImGuiWindow
2605
{
2606
    ImGuiContext*           Ctx;                                // Parent UI context (needs to be set explicitly by parent).
2607
    char*                   Name;                               // Window name, owned by the window.
2608
    ImGuiID                 ID;                                 // == ImHashStr(Name)
2609
    ImGuiWindowFlags        Flags;                              // See enum ImGuiWindowFlags_
2610
    ImGuiChildFlags         ChildFlags;                         // Set when window is a child window. See enum ImGuiChildFlags_
2611
    ImGuiViewportP*         Viewport;                           // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded.
2612
    ImVec2                  Pos;                                // Position (always rounded-up to nearest pixel)
2613
    ImVec2                  Size;                               // Current size (==SizeFull or collapsed title bar size)
2614
    ImVec2                  SizeFull;                           // Size when non collapsed
2615
    ImVec2                  ContentSize;                        // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding.
2616
    ImVec2                  ContentSizeIdeal;
2617
    ImVec2                  ContentSizeExplicit;                // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize().
2618
    ImVec2                  WindowPadding;                      // Window padding at the time of Begin().
2619
    float                   WindowRounding;                     // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.
2620
    float                   WindowBorderSize;                   // Window border size at the time of Begin().
2621
    float                   TitleBarHeight, MenuBarHeight;      // Note that those used to be function before 2024/05/28. If you have old code calling TitleBarHeight() you can change it to TitleBarHeight.
2622
    float                   DecoOuterSizeX1, DecoOuterSizeY1;   // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin().
2623
    float                   DecoOuterSizeX2, DecoOuterSizeY2;   // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y).
2624
    float                   DecoInnerSizeX1, DecoInnerSizeY1;   // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes).
2625
    int                     NameBufLen;                         // Size of buffer storing Name. May be larger than strlen(Name)!
2626
    ImGuiID                 MoveId;                             // == window->GetID("#MOVE")
2627
    ImGuiID                 ChildId;                            // ID of corresponding item in parent window (for navigation to return from child window to parent window)
2628
    ImGuiID                 PopupId;                            // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)
2629
    ImVec2                  Scroll;
2630
    ImVec2                  ScrollMax;
2631
    ImVec2                  ScrollTarget;                       // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
2632
    ImVec2                  ScrollTargetCenterRatio;            // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
2633
    ImVec2                  ScrollTargetEdgeSnapDist;           // 0.0f = no snapping, >0.0f snapping threshold
2634
    ImVec2                  ScrollbarSizes;                     // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.
2635
    bool                    ScrollbarX, ScrollbarY;             // Are scrollbars visible?
2636
    bool                    ScrollbarXStabilizeEnabled;         // Was ScrollbarX previously auto-stabilized?
2637
    ImU8                    ScrollbarXStabilizeToggledHistory;  // Used to stabilize scrollbar visibility in case of feedback loops
2638
    bool                    Active;                             // Set to true on Begin(), unless Collapsed
2639
    bool                    WasActive;
2640
    bool                    WriteAccessed;                      // Set to true when any widget access the current window
2641
    bool                    Collapsed;                          // Set when collapsing window to become only title-bar
2642
    bool                    WantCollapseToggle;
2643
    bool                    SkipItems;                          // Set when items can safely be all clipped (e.g. window not visible or collapsed)
2644
    bool                    SkipRefresh;                        // [EXPERIMENTAL] Reuse previous frame drawn contents, Begin() returns false.
2645
    bool                    Appearing;                          // Set during the frame where the window is appearing (or re-appearing)
2646
    bool                    Hidden;                             // Do not display (== HiddenFrames*** > 0)
2647
    bool                    IsFallbackWindow;                   // Set on the "Debug##Default" window.
2648
    bool                    IsExplicitChild;                    // Set when passed _ChildWindow, left to false by BeginDocked()
2649
    bool                    HasCloseButton;                     // Set when the window has a close button (p_open != NULL)
2650
    signed char             ResizeBorderHovered;                // Current border being hovered for resize (-1: none, otherwise 0-3)
2651
    signed char             ResizeBorderHeld;                   // Current border being held for resize (-1: none, otherwise 0-3)
2652
    short                   BeginCount;                         // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)
2653
    short                   BeginCountPreviousFrame;            // Number of Begin() during the previous frame
2654
    short                   BeginOrderWithinParent;             // Begin() order within immediate parent window, if we are a child window. Otherwise 0.
2655
    short                   BeginOrderWithinContext;            // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.
2656
    short                   FocusOrder;                         // Order within WindowsFocusOrder[], altered when windows are focused.
2657
    ImS8                    AutoFitFramesX, AutoFitFramesY;
2658
    bool                    AutoFitOnlyGrows;
2659
    ImGuiDir                AutoPosLastDirection;
2660
    ImS8                    HiddenFramesCanSkipItems;           // Hide the window for N frames
2661
    ImS8                    HiddenFramesCannotSkipItems;        // Hide the window for N frames while allowing items to be submitted so we can measure their size
2662
    ImS8                    HiddenFramesForRenderOnly;          // Hide the window until frame N at Render() time only
2663
    ImS8                    DisableInputsFrames;                // Disable window interactions for N frames
2664
    ImGuiCond               SetWindowPosAllowFlags : 8;         // store acceptable condition flags for SetNextWindowPos() use.
2665
    ImGuiCond               SetWindowSizeAllowFlags : 8;        // store acceptable condition flags for SetNextWindowSize() use.
2666
    ImGuiCond               SetWindowCollapsedAllowFlags : 8;   // store acceptable condition flags for SetNextWindowCollapsed() use.
2667
    ImVec2                  SetWindowPosVal;                    // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size)
2668
    ImVec2                  SetWindowPosPivot;                  // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right.
2669
2670
    ImVector<ImGuiID>       IDStack;                            // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)
2671
    ImGuiWindowTempData     DC;                                 // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name.
2672
2673
    // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer.
2674
    // The main 'OuterRect', omitted as a field, is window->Rect().
2675
    ImRect                  OuterRectClipped;                   // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.
2676
    ImRect                  InnerRect;                          // Inner rectangle (omit title bar, menu bar, scroll bar)
2677
    ImRect                  InnerClipRect;                      // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect.
2678
    ImRect                  WorkRect;                           // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).
2679
    ImRect                  ParentWorkRect;                     // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack?
2680
    ImRect                  ClipRect;                           // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back().
2681
    ImRect                  ContentRegionRect;                  // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.
2682
    ImVec2ih                HitTestHoleSize;                    // Define an optional rectangular hole where mouse will pass-through the window.
2683
    ImVec2ih                HitTestHoleOffset;
2684
2685
    int                     LastFrameActive;                    // Last frame number the window was Active.
2686
    float                   LastTimeActive;                     // Last timestamp the window was Active (using float as we don't need high precision there)
2687
    float                   ItemWidthDefault;
2688
    ImGuiStorage            StateStorage;
2689
    ImVector<ImGuiOldColumns> ColumnsStorage;
2690
    float                   FontWindowScale;                    // User scale multiplier per-window, via SetWindowFontScale()
2691
    float                   FontWindowScaleParents;
2692
    float                   FontRefSize;                        // This is a copy of window->CalcFontSize() at the time of Begin(), trying to phase out CalcFontSize() especially as it may be called on non-current window.
2693
    int                     SettingsOffset;                     // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)
2694
2695
    ImDrawList*             DrawList;                           // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)
2696
    ImDrawList              DrawListInst;
2697
    ImGuiWindow*            ParentWindow;                       // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL.
2698
    ImGuiWindow*            ParentWindowInBeginStack;
2699
    ImGuiWindow*            RootWindow;                         // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.
2700
    ImGuiWindow*            RootWindowPopupTree;                // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
2701
    ImGuiWindow*            RootWindowForTitleBarHighlight;     // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
2702
    ImGuiWindow*            RootWindowForNav;                   // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
2703
    ImGuiWindow*            ParentWindowForFocusRoute;          // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document)
2704
2705
    ImGuiWindow*            NavLastChildNavWindow;              // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)
2706
    ImGuiID                 NavLastIds[ImGuiNavLayer_COUNT];    // Last known NavId for this window, per layer (0/1)
2707
    ImRect                  NavRectRel[ImGuiNavLayer_COUNT];    // Reference rectangle, in window relative space
2708
    ImVec2                  NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX.
2709
    ImGuiID                 NavRootFocusScopeId;                // Focus Scope ID at the time of Begin()
2710
2711
    int                     MemoryDrawListIdxCapacity;          // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy
2712
    int                     MemoryDrawListVtxCapacity;
2713
    bool                    MemoryCompacted;                    // Set when window extraneous data have been garbage collected
2714
2715
public:
2716
    ImGuiWindow(ImGuiContext* context, const char* name);
2717
    ~ImGuiWindow();
2718
2719
    ImGuiID     GetID(const char* str, const char* str_end = NULL);
2720
    ImGuiID     GetID(const void* ptr);
2721
    ImGuiID     GetID(int n);
2722
    ImGuiID     GetIDFromPos(const ImVec2& p_abs);
2723
    ImGuiID     GetIDFromRectangle(const ImRect& r_abs);
2724
2725
    // We don't use g.FontSize because the window may be != g.CurrentWindow.
2726
242k
    ImRect      Rect() const            { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }
2727
242k
    ImRect      TitleBarRect() const    { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); }
2728
161k
    ImRect      MenuBarRect() const     { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); }
2729
2730
    // [Obsolete] ImGuiWindow::CalcFontSize() was removed in 1.92.x because error-prone/misleading. You can use window->FontRefSize for a copy of g.FontSize at the time of the last Begin() call for this window.
2731
    //float     CalcFontSize() const    { ImGuiContext& g = *Ctx; return g.FontSizeBase * FontWindowScale * FontWindowScaleParents;
2732
};
2733
2734
//-----------------------------------------------------------------------------
2735
// [SECTION] Tab bar, Tab item support
2736
//-----------------------------------------------------------------------------
2737
2738
// Extend ImGuiTabBarFlags_
2739
enum ImGuiTabBarFlagsPrivate_
2740
{
2741
    ImGuiTabBarFlags_DockNode                   = 1 << 20,  // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around]
2742
    ImGuiTabBarFlags_IsFocused                  = 1 << 21,
2743
    ImGuiTabBarFlags_SaveSettings               = 1 << 22,  // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs
2744
};
2745
2746
// Extend ImGuiTabItemFlags_
2747
enum ImGuiTabItemFlagsPrivate_
2748
{
2749
    ImGuiTabItemFlags_SectionMask_              = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing,
2750
    ImGuiTabItemFlags_NoCloseButton             = 1 << 20,  // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)
2751
    ImGuiTabItemFlags_Button                    = 1 << 21,  // Used by TabItemButton, change the tab item behavior to mimic a button
2752
    ImGuiTabItemFlags_Invisible                 = 1 << 22,  // To reserve space e.g. with ImGuiTabItemFlags_Leading
2753
    //ImGuiTabItemFlags_Unsorted                = 1 << 23,  // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window.
2754
};
2755
2756
// Storage for one active tab item (sizeof() 40 bytes)
2757
struct ImGuiTabItem
2758
{
2759
    ImGuiID             ID;
2760
    ImGuiTabItemFlags   Flags;
2761
    int                 LastFrameVisible;
2762
    int                 LastFrameSelected;      // This allows us to infer an ordered list of the last activated tabs with little maintenance
2763
    float               Offset;                 // Position relative to beginning of tab
2764
    float               Width;                  // Width currently displayed
2765
    float               ContentWidth;           // Width of label + padding, stored during BeginTabItem() call (misnamed as "Content" would normally imply width of label only)
2766
    float               RequestedWidth;         // Width optionally requested by caller, -1.0f is unused
2767
    ImS32               NameOffset;             // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames
2768
    ImS16               BeginOrder;             // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable
2769
    ImS16               IndexDuringLayout;      // Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions.
2770
    bool                WantClose;              // Marked as closed by SetTabItemClosed()
2771
2772
0
    ImGuiTabItem()      { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; }
2773
};
2774
2775
// Storage for a tab bar (sizeof() 160 bytes)
2776
struct IMGUI_API ImGuiTabBar
2777
{
2778
    ImGuiWindow*        Window;
2779
    ImVector<ImGuiTabItem> Tabs;
2780
    ImGuiTabBarFlags    Flags;
2781
    ImGuiID             ID;                     // Zero for tab-bars used by docking
2782
    ImGuiID             SelectedTabId;          // Selected tab/window
2783
    ImGuiID             NextSelectedTabId;      // Next selected tab/window. Will also trigger a scrolling animation
2784
    ImGuiID             VisibleTabId;           // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview)
2785
    int                 CurrFrameVisible;
2786
    int                 PrevFrameVisible;
2787
    ImRect              BarRect;
2788
    float               BarRectPrevWidth;       // Backup of previous width. When width change we enforce keep horizontal scroll on focused tab.
2789
    float               CurrTabsContentsHeight;
2790
    float               PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar
2791
    float               WidthAllTabs;           // Actual width of all tabs (locked during layout)
2792
    float               WidthAllTabsIdeal;      // Ideal width if all tabs were visible and not clipped
2793
    float               ScrollingAnim;
2794
    float               ScrollingTarget;
2795
    float               ScrollingTargetDistToVisibility;
2796
    float               ScrollingSpeed;
2797
    float               ScrollingRectMinX;
2798
    float               ScrollingRectMaxX;
2799
    float               SeparatorMinX;
2800
    float               SeparatorMaxX;
2801
    ImGuiID             ReorderRequestTabId;
2802
    ImS16               ReorderRequestOffset;
2803
    ImS8                BeginCount;
2804
    bool                WantLayout;
2805
    bool                VisibleTabWasSubmitted;
2806
    bool                TabsAddedNew;           // Set to true when a new tab item or button has been added to the tab bar during last frame
2807
    bool                ScrollButtonEnabled;
2808
    ImS16               TabsActiveCount;        // Number of tabs submitted this frame.
2809
    ImS16               LastTabItemIdx;         // Index of last BeginTabItem() tab for use by EndTabItem()
2810
    float               ItemSpacingY;
2811
    ImVec2              FramePadding;           // style.FramePadding locked at the time of BeginTabBar()
2812
    ImVec2              BackupCursorPos;
2813
    ImGuiTextBuffer     TabsNames;              // For non-docking tab bar we re-append names in a contiguous buffer.
2814
2815
    ImGuiTabBar();
2816
};
2817
2818
//-----------------------------------------------------------------------------
2819
// [SECTION] Table support
2820
//-----------------------------------------------------------------------------
2821
2822
0
#define IM_COL32_DISABLE                IM_COL32(0,0,0,1)   // Special sentinel code which cannot be used as a regular color.
2823
#define IMGUI_TABLE_MAX_COLUMNS         512                 // Arbitrary "safety" maximum, may be lifted in the future if needed. Must fit in ImGuiTableColumnIdx/ImGuiTableDrawChannelIdx.
2824
2825
// [Internal] sizeof() ~ 112
2826
// We use the terminology "Enabled" to refer to a column that is not Hidden by user/api.
2827
// We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping.
2828
// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped".
2829
struct ImGuiTableColumn
2830
{
2831
    ImGuiTableColumnFlags   Flags;                          // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_
2832
    float                   WidthGiven;                     // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space.
2833
    float                   MinX;                           // Absolute positions
2834
    float                   MaxX;
2835
    float                   WidthRequest;                   // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()
2836
    float                   WidthAuto;                      // Automatic width
2837
    float                   WidthMax;                       // Maximum width (FIXME: overwritten by each instance)
2838
    float                   StretchWeight;                  // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.
2839
    float                   InitStretchWeightOrWidth;       // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).
2840
    ImRect                  ClipRect;                       // Clipping rectangle for the column
2841
    ImGuiID                 UserID;                         // Optional, value passed to TableSetupColumn()
2842
    float                   WorkMinX;                       // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column
2843
    float                   WorkMaxX;                       // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2)
2844
    float                   ItemWidth;                      // Current item width for the column, preserved across rows
2845
    float                   ContentMaxXFrozen;              // Contents maximum position for frozen rows (apart from headers), from which we can infer content width.
2846
    float                   ContentMaxXUnfrozen;
2847
    float                   ContentMaxXHeadersUsed;         // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls
2848
    float                   ContentMaxXHeadersIdeal;
2849
    ImS16                   NameOffset;                     // Offset into parent ColumnsNames[]
2850
    ImGuiTableColumnIdx     DisplayOrder;                   // Index within Table's IndexToDisplayOrder[] (column may be reordered by users)
2851
    ImGuiTableColumnIdx     IndexWithinEnabledSet;          // Index within enabled/visible set (<= IndexToDisplayOrder)
2852
    ImGuiTableColumnIdx     PrevEnabledColumn;              // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column
2853
    ImGuiTableColumnIdx     NextEnabledColumn;              // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column
2854
    ImGuiTableColumnIdx     SortOrder;                      // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort
2855
    ImGuiTableDrawChannelIdx DrawChannelCurrent;            // Index within DrawSplitter.Channels[]
2856
    ImGuiTableDrawChannelIdx DrawChannelFrozen;             // Draw channels for frozen rows (often headers)
2857
    ImGuiTableDrawChannelIdx DrawChannelUnfrozen;           // Draw channels for unfrozen rows
2858
    bool                    IsEnabled;                      // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0
2859
    bool                    IsUserEnabled;                  // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling).
2860
    bool                    IsUserEnabledNextFrame;
2861
    bool                    IsVisibleX;                     // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled).
2862
    bool                    IsVisibleY;
2863
    bool                    IsRequestOutput;                // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.
2864
    bool                    IsSkipItems;                    // Do we want item submissions to this column to be completely ignored (no layout will happen).
2865
    bool                    IsPreserveWidthAuto;
2866
    ImS8                    NavLayerCurrent;                // ImGuiNavLayer in 1 byte
2867
    ImU8                    AutoFitQueue;                   // Queue of 8 values for the next 8 frames to request auto-fit
2868
    ImU8                    CannotSkipItemsQueue;           // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem
2869
    ImU8                    SortDirection : 2;              // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending
2870
    ImU8                    SortDirectionsAvailCount : 2;   // Number of available sort directions (0 to 3)
2871
    ImU8                    SortDirectionsAvailMask : 4;    // Mask of available sort directions (1-bit each)
2872
    ImU8                    SortDirectionsAvailList;        // Ordered list of available sort directions (2-bits each, total 8-bits)
2873
2874
    ImGuiTableColumn()
2875
0
    {
2876
0
        memset(this, 0, sizeof(*this));
2877
0
        StretchWeight = WidthRequest = -1.0f;
2878
0
        NameOffset = -1;
2879
0
        DisplayOrder = IndexWithinEnabledSet = -1;
2880
0
        PrevEnabledColumn = NextEnabledColumn = -1;
2881
0
        SortOrder = -1;
2882
0
        SortDirection = ImGuiSortDirection_None;
2883
0
        DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1;
2884
0
    }
2885
};
2886
2887
// Transient cell data stored per row.
2888
// sizeof() ~ 6 bytes
2889
struct ImGuiTableCellData
2890
{
2891
    ImU32                       BgColor;    // Actual color
2892
    ImGuiTableColumnIdx         Column;     // Column number
2893
};
2894
2895
// Parameters for TableAngledHeadersRowEx()
2896
// This may end up being refactored for more general purpose.
2897
// sizeof() ~ 12 bytes
2898
struct ImGuiTableHeaderData
2899
{
2900
    ImGuiTableColumnIdx         Index;      // Column index
2901
    ImU32                       TextColor;
2902
    ImU32                       BgColor0;
2903
    ImU32                       BgColor1;
2904
};
2905
2906
// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?)
2907
// sizeof() ~ 24 bytes
2908
struct ImGuiTableInstanceData
2909
{
2910
    ImGuiID                     TableInstanceID;
2911
    float                       LastOuterHeight;            // Outer height from last frame
2912
    float                       LastTopHeadersRowHeight;    // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set)
2913
    float                       LastFrozenHeight;           // Height of frozen section from last frame
2914
    int                         HoveredRowLast;             // Index of row which was hovered last frame.
2915
    int                         HoveredRowNext;             // Index of row hovered this frame, set after encountering it.
2916
2917
0
    ImGuiTableInstanceData()    { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; }
2918
};
2919
2920
// sizeof() ~ 592 bytes + heap allocs described in TableBeginInitMemory()
2921
struct IMGUI_API ImGuiTable
2922
{
2923
    ImGuiID                     ID;
2924
    ImGuiTableFlags             Flags;
2925
    void*                       RawData;                    // Single allocation to hold Columns[], DisplayOrderToIndex[], and RowCellData[]
2926
    ImGuiTableTempData*         TempData;                   // Transient data while table is active. Point within g.CurrentTableStack[]
2927
    ImSpan<ImGuiTableColumn>    Columns;                    // Point within RawData[]
2928
    ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex;        // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)
2929
    ImSpan<ImGuiTableCellData>  RowCellData;                // Point within RawData[]. Store cells background requests for current row.
2930
    ImBitArrayPtr               EnabledMaskByDisplayOrder;  // Column DisplayOrder -> IsEnabled map
2931
    ImBitArrayPtr               EnabledMaskByIndex;         // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data
2932
    ImBitArrayPtr               VisibleMaskByIndex;         // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect)
2933
    ImGuiTableFlags             SettingsLoadedFlags;        // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)
2934
    int                         SettingsOffset;             // Offset in g.SettingsTables
2935
    int                         LastFrameActive;
2936
    int                         ColumnsCount;               // Number of columns declared in BeginTable()
2937
    int                         CurrentRow;
2938
    int                         CurrentColumn;
2939
    ImS16                       InstanceCurrent;            // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple tables with the same ID are multiple tables, they are just synced.
2940
    ImS16                       InstanceInteracted;         // Mark which instance (generally 0) of the same ID is being interacted with
2941
    float                       RowPosY1;
2942
    float                       RowPosY2;
2943
    float                       RowMinHeight;               // Height submitted to TableNextRow()
2944
    float                       RowCellPaddingY;            // Top and bottom padding. Reloaded during row change.
2945
    float                       RowTextBaseline;
2946
    float                       RowIndentOffsetX;
2947
    ImGuiTableRowFlags          RowFlags : 16;              // Current row flags, see ImGuiTableRowFlags_
2948
    ImGuiTableRowFlags          LastRowFlags : 16;
2949
    int                         RowBgColorCounter;          // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this.
2950
    ImU32                       RowBgColor[2];              // Background color override for current row.
2951
    ImU32                       BorderColorStrong;
2952
    ImU32                       BorderColorLight;
2953
    float                       BorderX1;
2954
    float                       BorderX2;
2955
    float                       HostIndentX;
2956
    float                       MinColumnWidth;
2957
    float                       OuterPaddingX;
2958
    float                       CellPaddingX;               // Padding from each borders. Locked in BeginTable()/Layout.
2959
    float                       CellSpacingX1;              // Spacing between non-bordered cells. Locked in BeginTable()/Layout.
2960
    float                       CellSpacingX2;
2961
    float                       InnerWidth;                 // User value passed to BeginTable(), see comments at the top of BeginTable() for details.
2962
    float                       ColumnsGivenWidth;          // Sum of current column width
2963
    float                       ColumnsAutoFitWidth;        // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window
2964
    float                       ColumnsStretchSumWeights;   // Sum of weight of all enabled stretching columns
2965
    float                       ResizedColumnNextWidth;
2966
    float                       ResizeLockMinContentsX2;    // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.
2967
    float                       RefScale;                   // Reference scale to be able to rescale columns on font/dpi changes.
2968
    float                       AngledHeadersHeight;        // Set by TableAngledHeadersRow(), used in TableUpdateLayout()
2969
    float                       AngledHeadersSlope;         // Set by TableAngledHeadersRow(), used in TableUpdateLayout()
2970
    ImRect                      OuterRect;                  // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().
2971
    ImRect                      InnerRect;                  // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is "
2972
    ImRect                      WorkRect;
2973
    ImRect                      InnerClipRect;
2974
    ImRect                      BgClipRect;                 // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries
2975
    ImRect                      Bg0ClipRectForDrawCmd;      // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped
2976
    ImRect                      Bg2ClipRectForDrawCmd;      // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.
2977
    ImRect                      HostClipRect;               // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.
2978
    ImRect                      HostBackupInnerClipRect;    // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()
2979
    ImGuiWindow*                OuterWindow;                // Parent window for the table
2980
    ImGuiWindow*                InnerWindow;                // Window holding the table data (== OuterWindow or a child window)
2981
    ImGuiTextBuffer             ColumnsNames;               // Contiguous buffer holding columns names
2982
    ImDrawListSplitter*         DrawSplitter;               // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly
2983
    ImGuiTableInstanceData      InstanceDataFirst;
2984
    ImVector<ImGuiTableInstanceData>    InstanceDataExtra;  // FIXME-OPT: Using a small-vector pattern would be good.
2985
    ImGuiTableColumnSortSpecs   SortSpecsSingle;
2986
    ImVector<ImGuiTableColumnSortSpecs> SortSpecsMulti;     // FIXME-OPT: Using a small-vector pattern would be good.
2987
    ImGuiTableSortSpecs         SortSpecs;                  // Public facing sorts specs, this is what we return in TableGetSortSpecs()
2988
    ImGuiTableColumnIdx         SortSpecsCount;
2989
    ImGuiTableColumnIdx         ColumnsEnabledCount;        // Number of enabled columns (<= ColumnsCount)
2990
    ImGuiTableColumnIdx         ColumnsEnabledFixedCount;   // Number of enabled columns using fixed width (<= ColumnsCount)
2991
    ImGuiTableColumnIdx         DeclColumnsCount;           // Count calls to TableSetupColumn()
2992
    ImGuiTableColumnIdx         AngledHeadersCount;         // Count columns with angled headers
2993
    ImGuiTableColumnIdx         HoveredColumnBody;          // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!
2994
    ImGuiTableColumnIdx         HoveredColumnBorder;        // Index of column whose right-border is being hovered (for resizing).
2995
    ImGuiTableColumnIdx         HighlightColumnHeader;      // Index of column which should be highlighted.
2996
    ImGuiTableColumnIdx         AutoFitSingleColumn;        // Index of single column requesting auto-fit.
2997
    ImGuiTableColumnIdx         ResizedColumn;              // Index of column being resized. Reset when InstanceCurrent==0.
2998
    ImGuiTableColumnIdx         LastResizedColumn;          // Index of column being resized from previous frame.
2999
    ImGuiTableColumnIdx         HeldHeaderColumn;           // Index of column header being held.
3000
    ImGuiTableColumnIdx         ReorderColumn;              // Index of column being reordered. (not cleared)
3001
    ImGuiTableColumnIdx         ReorderColumnDir;           // -1 or +1
3002
    ImGuiTableColumnIdx         LeftMostEnabledColumn;      // Index of left-most non-hidden column.
3003
    ImGuiTableColumnIdx         RightMostEnabledColumn;     // Index of right-most non-hidden column.
3004
    ImGuiTableColumnIdx         LeftMostStretchedColumn;    // Index of left-most stretched column.
3005
    ImGuiTableColumnIdx         RightMostStretchedColumn;   // Index of right-most stretched column.
3006
    ImGuiTableColumnIdx         ContextPopupColumn;         // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot
3007
    ImGuiTableColumnIdx         FreezeRowsRequest;          // Requested frozen rows count
3008
    ImGuiTableColumnIdx         FreezeRowsCount;            // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset)
3009
    ImGuiTableColumnIdx         FreezeColumnsRequest;       // Requested frozen columns count
3010
    ImGuiTableColumnIdx         FreezeColumnsCount;         // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset)
3011
    ImGuiTableColumnIdx         RowCellDataCurrent;         // Index of current RowCellData[] entry in current row
3012
    ImGuiTableDrawChannelIdx    DummyDrawChannel;           // Redirect non-visible columns here.
3013
    ImGuiTableDrawChannelIdx    Bg2DrawChannelCurrent;      // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[]
3014
    ImGuiTableDrawChannelIdx    Bg2DrawChannelUnfrozen;
3015
    ImS8                        NavLayer;                   // ImGuiNavLayer at the time of BeginTable().
3016
    bool                        IsLayoutLocked;             // Set by TableUpdateLayout() which is called when beginning the first row.
3017
    bool                        IsInsideRow;                // Set when inside TableBeginRow()/TableEndRow().
3018
    bool                        IsInitializing;
3019
    bool                        IsSortSpecsDirty;
3020
    bool                        IsUsingHeaders;             // Set when the first row had the ImGuiTableRowFlags_Headers flag.
3021
    bool                        IsContextPopupOpen;         // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).
3022
    bool                        DisableDefaultContextMenu;  // Disable default context menu. You may submit your own using TableBeginContextMenuPopup()/EndPopup()
3023
    bool                        IsSettingsRequestLoad;
3024
    bool                        IsSettingsDirty;            // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.
3025
    bool                        IsDefaultDisplayOrder;      // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)
3026
    bool                        IsResetAllRequest;
3027
    bool                        IsResetDisplayOrderRequest;
3028
    bool                        IsUnfrozenRows;             // Set when we got past the frozen row.
3029
    bool                        IsDefaultSizingPolicy;      // Set if user didn't explicitly set a sizing policy in BeginTable()
3030
    bool                        IsActiveIdAliveBeforeTable;
3031
    bool                        IsActiveIdInTable;
3032
    bool                        HasScrollbarYCurr;          // Whether ANY instance of this table had a vertical scrollbar during the current frame.
3033
    bool                        HasScrollbarYPrev;          // Whether ANY instance of this table had a vertical scrollbar during the previous.
3034
    bool                        MemoryCompacted;
3035
    bool                        HostSkipItems;              // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis
3036
3037
0
    ImGuiTable()                { memset(this, 0, sizeof(*this)); LastFrameActive = -1; }
3038
0
    ~ImGuiTable()               { IM_FREE(RawData); }
3039
};
3040
3041
// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).
3042
// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.
3043
// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.
3044
// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs.
3045
// sizeof() ~ 136 bytes.
3046
struct IMGUI_API ImGuiTableTempData
3047
{
3048
    int                         TableIndex;                 // Index in g.Tables.Buf[] pool
3049
    float                       LastTimeActive;             // Last timestamp this structure was used
3050
    float                       AngledHeadersExtraWidth;    // Used in EndTable()
3051
    ImVector<ImGuiTableHeaderData> AngledHeadersRequests;   // Used in TableAngledHeadersRow()
3052
3053
    ImVec2                      UserOuterSize;              // outer_size.x passed to BeginTable()
3054
    ImDrawListSplitter          DrawSplitter;
3055
3056
    ImRect                      HostBackupWorkRect;         // Backup of InnerWindow->WorkRect at the end of BeginTable()
3057
    ImRect                      HostBackupParentWorkRect;   // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()
3058
    ImVec2                      HostBackupPrevLineSize;     // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable()
3059
    ImVec2                      HostBackupCurrLineSize;     // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable()
3060
    ImVec2                      HostBackupCursorMaxPos;     // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()
3061
    ImVec1                      HostBackupColumnsOffset;    // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable()
3062
    float                       HostBackupItemWidth;        // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable()
3063
    int                         HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable()
3064
3065
0
    ImGuiTableTempData()        { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; }
3066
};
3067
3068
// sizeof() ~ 16
3069
struct ImGuiTableColumnSettings
3070
{
3071
    float                   WidthOrWeight;
3072
    ImGuiID                 UserID;
3073
    ImGuiTableColumnIdx     Index;
3074
    ImGuiTableColumnIdx     DisplayOrder;
3075
    ImGuiTableColumnIdx     SortOrder;
3076
    ImU8                    SortDirection : 2;
3077
    ImS8                    IsEnabled : 2; // "Visible" in ini file
3078
    ImU8                    IsStretch : 1;
3079
3080
    ImGuiTableColumnSettings()
3081
104
    {
3082
104
        WidthOrWeight = 0.0f;
3083
104
        UserID = 0;
3084
104
        Index = -1;
3085
104
        DisplayOrder = SortOrder = -1;
3086
104
        SortDirection = ImGuiSortDirection_None;
3087
104
        IsEnabled = -1;
3088
104
        IsStretch = 0;
3089
104
    }
3090
};
3091
3092
// This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.)
3093
struct ImGuiTableSettings
3094
{
3095
    ImGuiID                     ID;                     // Set to 0 to invalidate/delete the setting
3096
    ImGuiTableFlags             SaveFlags;              // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..)
3097
    float                       RefScale;               // Reference scale to be able to rescale columns on font/dpi changes.
3098
    ImGuiTableColumnIdx         ColumnsCount;
3099
    ImGuiTableColumnIdx         ColumnsCountMax;        // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher
3100
    bool                        WantApply;              // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)
3101
3102
26
    ImGuiTableSettings()        { memset(this, 0, sizeof(*this)); }
3103
78
    ImGuiTableColumnSettings*   GetColumnSettings()     { return (ImGuiTableColumnSettings*)(this + 1); }
3104
};
3105
3106
//-----------------------------------------------------------------------------
3107
// [SECTION] ImGui internal API
3108
// No guarantee of forward compatibility here!
3109
//-----------------------------------------------------------------------------
3110
3111
namespace ImGui
3112
{
3113
    // Windows
3114
    // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window)
3115
    // If this ever crashes because g.CurrentWindow is NULL, it means that either:
3116
    // - ImGui::NewFrame() has never been called, which is illegal.
3117
    // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.
3118
    IMGUI_API ImGuiIO&         GetIO(ImGuiContext* ctx);
3119
    IMGUI_API ImGuiPlatformIO& GetPlatformIO(ImGuiContext* ctx);
3120
0
    inline    ImGuiWindow*  GetCurrentWindowRead()      { ImGuiContext& g = *GImGui; return g.CurrentWindow; }
3121
1.70M
    inline    ImGuiWindow*  GetCurrentWindow()          { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }
3122
    IMGUI_API ImGuiWindow*  FindWindowByID(ImGuiID id);
3123
    IMGUI_API ImGuiWindow*  FindWindowByName(const char* name);
3124
    IMGUI_API void          UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
3125
    IMGUI_API void          UpdateWindowSkipRefresh(ImGuiWindow* window);
3126
    IMGUI_API ImVec2        CalcWindowNextAutoFitSize(ImGuiWindow* window);
3127
    IMGUI_API bool          IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy);
3128
    IMGUI_API bool          IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
3129
    IMGUI_API bool          IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);
3130
    IMGUI_API bool          IsWindowNavFocusable(ImGuiWindow* window);
3131
    IMGUI_API void          SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
3132
    IMGUI_API void          SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);
3133
    IMGUI_API void          SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0);
3134
    IMGUI_API void          SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size);
3135
    IMGUI_API void          SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window);
3136
0
    inline void             SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; }
3137
1.06k
    inline ImRect           WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }
3138
662
    inline ImRect           WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }
3139
0
    inline ImVec2           WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p)  { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); }
3140
0
    inline ImVec2           WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p)  { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }
3141
3142
    // Windows: Display Order and Focus Order
3143
    IMGUI_API void          FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0);
3144
    IMGUI_API void          FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags);
3145
    IMGUI_API void          BringWindowToFocusFront(ImGuiWindow* window);
3146
    IMGUI_API void          BringWindowToDisplayFront(ImGuiWindow* window);
3147
    IMGUI_API void          BringWindowToDisplayBack(ImGuiWindow* window);
3148
    IMGUI_API void          BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window);
3149
    IMGUI_API int           FindWindowDisplayIndex(ImGuiWindow* window);
3150
    IMGUI_API ImGuiWindow*  FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window);
3151
3152
    // Windows: Idle, Refresh Policies [EXPERIMENTAL]
3153
    IMGUI_API void          SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags);
3154
3155
    // Fonts, drawing
3156
    IMGUI_API void          RegisterUserTexture(ImTextureData* tex); // Register external texture. EXPERIMENTAL: DO NOT USE YET.
3157
    IMGUI_API void          UnregisterUserTexture(ImTextureData* tex);
3158
    IMGUI_API void          RegisterFontAtlas(ImFontAtlas* atlas);
3159
    IMGUI_API void          UnregisterFontAtlas(ImFontAtlas* atlas);
3160
    IMGUI_API void          SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling);
3161
    IMGUI_API void          UpdateCurrentFontSize(float restore_font_size_after_scaling);
3162
    IMGUI_API void          SetFontRasterizerDensity(float rasterizer_density);
3163
0
    inline float            GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; }
3164
1.29M
    inline float            GetRoundedFontSize(float size) { return IM_ROUND(size); }
3165
    IMGUI_API ImFont*       GetDefaultFont();
3166
    IMGUI_API void          PushPasswordFont();
3167
    IMGUI_API void          PopPasswordFont();
3168
0
    inline ImDrawList*      GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.
3169
    IMGUI_API ImDrawList*   GetBackgroundDrawList(ImGuiViewport* viewport);                     // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.
3170
    IMGUI_API ImDrawList*   GetForegroundDrawList(ImGuiViewport* viewport);                     // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.
3171
    IMGUI_API void          AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);
3172
3173
    // Init
3174
    IMGUI_API void          Initialize();
3175
    IMGUI_API void          Shutdown();    // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().
3176
3177
    // NewFrame
3178
    IMGUI_API void          UpdateInputEvents(bool trickle_fast_inputs);
3179
    IMGUI_API void          UpdateHoveredWindowAndCaptureFlags(const ImVec2& mouse_pos);
3180
    IMGUI_API void          FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window);
3181
    IMGUI_API void          StartMouseMovingWindow(ImGuiWindow* window);
3182
    IMGUI_API void          StopMouseMovingWindow();
3183
    IMGUI_API void          UpdateMouseMovingWindowNewFrame();
3184
    IMGUI_API void          UpdateMouseMovingWindowEndFrame();
3185
3186
    // Generic context hooks
3187
    IMGUI_API ImGuiID       AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook);
3188
    IMGUI_API void          RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove);
3189
    IMGUI_API void          CallContextHooks(ImGuiContext* context, ImGuiContextHookType type);
3190
3191
    // Viewports
3192
    IMGUI_API void          ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale);
3193
    IMGUI_API void          SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport);
3194
3195
    // Settings
3196
    IMGUI_API void                  MarkIniSettingsDirty();
3197
    IMGUI_API void                  MarkIniSettingsDirty(ImGuiWindow* window);
3198
    IMGUI_API void                  ClearIniSettings();
3199
    IMGUI_API void                  AddSettingsHandler(const ImGuiSettingsHandler* handler);
3200
    IMGUI_API void                  RemoveSettingsHandler(const char* type_name);
3201
    IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);
3202
3203
    // Settings - Windows
3204
    IMGUI_API ImGuiWindowSettings*  CreateNewWindowSettings(const char* name);
3205
    IMGUI_API ImGuiWindowSettings*  FindWindowSettingsByID(ImGuiID id);
3206
    IMGUI_API ImGuiWindowSettings*  FindWindowSettingsByWindow(ImGuiWindow* window);
3207
    IMGUI_API void                  ClearWindowSettings(const char* name);
3208
3209
    // Localization
3210
    IMGUI_API void          LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count);
3211
0
    inline const char*      LocalizeGetMsg(ImGuiLocKey key) { ImGuiContext& g = *GImGui; const char* msg = g.LocalizationTable[key]; return msg ? msg : "*Missing Text*"; }
3212
3213
    // Scrolling
3214
    IMGUI_API void          SetScrollX(ImGuiWindow* window, float scroll_x);
3215
    IMGUI_API void          SetScrollY(ImGuiWindow* window, float scroll_y);
3216
    IMGUI_API void          SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio);
3217
    IMGUI_API void          SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio);
3218
3219
    // Early work-in-progress API (ScrollToItem() will become public)
3220
    IMGUI_API void          ScrollToItem(ImGuiScrollFlags flags = 0);
3221
    IMGUI_API void          ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);
3222
    IMGUI_API ImVec2        ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);
3223
//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3224
0
    inline void             ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); }
3225
//#endif
3226
3227
    // Basic Accessors
3228
0
    inline ImGuiItemStatusFlags GetItemStatusFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; }
3229
0
    inline ImGuiItemFlags   GetItemFlags()  { ImGuiContext& g = *GImGui; return g.LastItemData.ItemFlags; }
3230
0
    inline ImGuiID          GetActiveID()   { ImGuiContext& g = *GImGui; return g.ActiveId; }
3231
0
    inline ImGuiID          GetFocusID()    { ImGuiContext& g = *GImGui; return g.NavId; }
3232
    IMGUI_API void          SetActiveID(ImGuiID id, ImGuiWindow* window);
3233
    IMGUI_API void          SetFocusID(ImGuiID id, ImGuiWindow* window);
3234
    IMGUI_API void          ClearActiveID();
3235
    IMGUI_API ImGuiID       GetHoveredID();
3236
    IMGUI_API void          SetHoveredID(ImGuiID id);
3237
    IMGUI_API void          KeepAliveID(ImGuiID id);
3238
    IMGUI_API void          MarkItemEdited(ImGuiID id);     // Mark data associated to given item as "edited", used by IsItemDeactivatedAfterEdit() function.
3239
    IMGUI_API void          PushOverrideID(ImGuiID id);     // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes)
3240
    IMGUI_API ImGuiID       GetIDWithSeed(const char* str_id_begin, const char* str_id_end, ImGuiID seed);
3241
    IMGUI_API ImGuiID       GetIDWithSeed(int n, ImGuiID seed);
3242
3243
    // Basic Helpers for widget code
3244
    IMGUI_API void          ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);
3245
0
    inline void             ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.
3246
    IMGUI_API bool          ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);
3247
    IMGUI_API bool          ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags);
3248
    IMGUI_API bool          IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0);
3249
    IMGUI_API bool          IsClippedEx(const ImRect& bb, ImGuiID id);
3250
    IMGUI_API void          SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);
3251
    IMGUI_API ImVec2        CalcItemSize(ImVec2 size, float default_w, float default_h);
3252
    IMGUI_API float         CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
3253
    IMGUI_API void          PushMultiItemsWidths(int components, float width_full);
3254
    IMGUI_API void          ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min);
3255
    IMGUI_API void          CalcClipRectVisibleItemsY(const ImRect& clip_rect, const ImVec2& pos, float items_height, int* out_visible_start, int* out_visible_end);
3256
3257
    // Parameter stacks (shared)
3258
    IMGUI_API const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx);
3259
    IMGUI_API void          BeginDisabledOverrideReenable();
3260
    IMGUI_API void          EndDisabledOverrideReenable();
3261
3262
    // Logging/Capture
3263
    IMGUI_API void          LogBegin(ImGuiLogFlags flags, int auto_open_depth);         // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.
3264
    IMGUI_API void          LogToBuffer(int auto_open_depth = -1);                      // Start logging/capturing to internal buffer
3265
    IMGUI_API void          LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL);
3266
    IMGUI_API void          LogSetNextTextDecoration(const char* prefix, const char* suffix);
3267
3268
    // Childs
3269
    IMGUI_API bool          BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags);
3270
3271
    // Popups, Modals
3272
    IMGUI_API bool          BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags);
3273
    IMGUI_API bool          BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags);
3274
    IMGUI_API void          OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);
3275
    IMGUI_API void          ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);
3276
    IMGUI_API void          ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);
3277
    IMGUI_API void          ClosePopupsExceptModals();
3278
    IMGUI_API bool          IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);
3279
    IMGUI_API ImRect        GetPopupAllowedExtentRect(ImGuiWindow* window);
3280
    IMGUI_API ImGuiWindow*  GetTopMostPopupModal();
3281
    IMGUI_API ImGuiWindow*  GetTopMostAndVisiblePopupModal();
3282
    IMGUI_API ImGuiWindow*  FindBlockingModal(ImGuiWindow* window);
3283
    IMGUI_API ImVec2        FindBestWindowPosForPopup(ImGuiWindow* window);
3284
    IMGUI_API ImVec2        FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);
3285
3286
    // Tooltips
3287
    IMGUI_API bool          BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);
3288
    IMGUI_API bool          BeginTooltipHidden();
3289
3290
    // Menus
3291
    IMGUI_API bool          BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);
3292
    IMGUI_API bool          BeginMenuEx(const char* label, const char* icon, bool enabled = true);
3293
    IMGUI_API bool          MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true);
3294
3295
    // Combos
3296
    IMGUI_API bool          BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags);
3297
    IMGUI_API bool          BeginComboPreview();
3298
    IMGUI_API void          EndComboPreview();
3299
3300
    // Keyboard/Gamepad Navigation
3301
    IMGUI_API void          NavInitWindow(ImGuiWindow* window, bool force_reinit);
3302
    IMGUI_API void          NavInitRequestApplyResult();
3303
    IMGUI_API bool          NavMoveRequestButNoResultYet();
3304
    IMGUI_API void          NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
3305
    IMGUI_API void          NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);
3306
    IMGUI_API void          NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);
3307
    IMGUI_API void          NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, const ImGuiTreeNodeStackData* tree_node_data);
3308
    IMGUI_API void          NavMoveRequestCancel();
3309
    IMGUI_API void          NavMoveRequestApplyResult();
3310
    IMGUI_API void          NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);
3311
    IMGUI_API void          NavHighlightActivated(ImGuiID id);
3312
    IMGUI_API void          NavClearPreferredPosForAxis(ImGuiAxis axis);
3313
    IMGUI_API void          SetNavCursorVisibleAfterMove();
3314
    IMGUI_API void          NavUpdateCurrentWindowIsScrollPushableX();
3315
    IMGUI_API void          SetNavWindow(ImGuiWindow* window);
3316
    IMGUI_API void          SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);
3317
    IMGUI_API void          SetNavFocusScope(ImGuiID focus_scope_id);
3318
3319
    // Focus/Activation
3320
    // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are
3321
    // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones.
3322
    IMGUI_API void          FocusItem();                    // Focus last item (no selection/activation).
3323
    IMGUI_API void          ActivateItemByID(ImGuiID id);   // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. Was called 'ActivateItem()' before 1.89.7.
3324
3325
    // Inputs
3326
    // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.
3327
2.15M
    inline bool             IsNamedKey(ImGuiKey key)                    { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }
3328
291k
    inline bool             IsNamedKeyOrMod(ImGuiKey key)               { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super; }
3329
1.49k
    inline bool             IsLegacyKey(ImGuiKey key)                   { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }
3330
272
    inline bool             IsKeyboardKey(ImGuiKey key)                 { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; }
3331
540
    inline bool             IsGamepadKey(ImGuiKey key)                  { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }
3332
0
    inline bool             IsMouseKey(ImGuiKey key)                    { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }
3333
571k
    inline bool             IsAliasKey(ImGuiKey key)                    { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }
3334
322k
    inline bool             IsLRModKey(ImGuiKey key)                    { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; }
3335
    ImGuiKeyChord           FixupKeyChord(ImGuiKeyChord key_chord);
3336
    inline ImGuiKey         ConvertSingleModFlagToKey(ImGuiKey key)
3337
705k
    {
3338
705k
        if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl;
3339
541k
        if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift;
3340
378k
        if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt;
3341
163k
        if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper;
3342
0
        return key;
3343
163k
    }
3344
3345
    IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key);
3346
1.86M
    inline ImGuiKeyData*    GetKeyData(ImGuiKey key)                                    { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); }
3347
    IMGUI_API const char*   GetKeyChordName(ImGuiKeyChord key_chord);
3348
402k
    inline ImGuiKey         MouseButtonToKey(ImGuiMouseButton button)                   { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); }
3349
    IMGUI_API bool          IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);
3350
    IMGUI_API ImVec2        GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down);
3351
    IMGUI_API float         GetNavTweakPressedAmount(ImGuiAxis axis);
3352
    IMGUI_API int           CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);
3353
    IMGUI_API void          GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate);
3354
    IMGUI_API void          TeleportMousePos(const ImVec2& pos);
3355
    IMGUI_API void          SetActiveIdUsingAllKeyboardKeys();
3356
2.64k
    inline bool             IsActiveIdUsingNavDir(ImGuiDir dir)                         { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }
3357
3358
    // [EXPERIMENTAL] Low-Level: Key/Input Ownership
3359
    // - The idea is that instead of "eating" a given input, we can link to an owner id.
3360
    // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).
3361
    // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_NoOwner (== -1) or a custom ID.
3362
    // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0).
3363
    // - Input ownership is automatically released on the frame after a key is released. Therefore:
3364
    //   - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).
3365
    //   - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover).
3366
    // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.
3367
    // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.
3368
    //   Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.
3369
    IMGUI_API ImGuiID       GetKeyOwner(ImGuiKey key);
3370
    IMGUI_API void          SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
3371
    IMGUI_API void          SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);
3372
    IMGUI_API void          SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags);       // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.
3373
    IMGUI_API bool          TestKeyOwner(ImGuiKey key, ImGuiID owner_id);               // Test that key is either not owned, either owned by 'owner_id'
3374
283k
    inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key)          { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }
3375
3376
    // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership
3377
    // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag.
3378
    // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0)
3379
    // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'.
3380
    //   Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.
3381
    // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.
3382
    IMGUI_API bool          IsKeyDown(ImGuiKey key, ImGuiID owner_id);
3383
    IMGUI_API bool          IsKeyPressed(ImGuiKey key, ImGuiInputFlags flags, ImGuiID owner_id = 0);    // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat.
3384
    IMGUI_API bool          IsKeyReleased(ImGuiKey key, ImGuiID owner_id);
3385
    IMGUI_API bool          IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id = 0);
3386
    IMGUI_API bool          IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);
3387
    IMGUI_API bool          IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0);
3388
    IMGUI_API bool          IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);
3389
    IMGUI_API bool          IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id);
3390
3391
    // Shortcut Testing & Routing
3392
    // - Set Shortcut() and SetNextItemShortcut() in imgui.h
3393
    // - When a policy (except for ImGuiInputFlags_RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(),
3394
    //   allowing the system to decide where to route the input among other route-aware calls.
3395
    //   (* using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key) and bypassing route registration and check)
3396
    // - When using one of the routing option:
3397
    //   - The default route is ImGuiInputFlags_RouteFocused (accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.)
3398
    //   - Routes are requested given a chord (key + modifiers) and a routing policy.
3399
    //   - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.
3400
    //   - Each route may be granted to a single owner. When multiple requests are made we have policies to select the winning route (e.g. deep most window).
3401
    //   - Multiple read sites may use the same owner id can all access the granted route.
3402
    //   - When owner_id is 0 we use the current Focus Scope ID as a owner ID in order to identify our location.
3403
    // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute()
3404
    //   e.g. if you have a tool window associated to a document, and you want document shortcuts to run when the tool is focused.
3405
    IMGUI_API bool          Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id);
3406
    IMGUI_API bool          SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID owner_id); // owner_id needs to be explicit and cannot be 0
3407
    IMGUI_API bool          TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);
3408
    IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord);
3409
3410
    // [EXPERIMENTAL] Focus Scope
3411
    // This is generally used to identify a unique input location (for e.g. a selection set)
3412
    // There is one per window (automatically set in Begin), but:
3413
    // - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set.
3414
    //   So in order to identify a set multiple lists in same window may each need a focus scope.
3415
    //   If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope()
3416
    // - Shortcut routing also use focus scope as a default location identifier if an owner is not provided.
3417
    // We don't use the ID Stack for this as it is common to want them separate.
3418
    IMGUI_API void          PushFocusScope(ImGuiID id);
3419
    IMGUI_API void          PopFocusScope();
3420
0
    inline ImGuiID          GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; }   // Focus scope we are outputting into, set by PushFocusScope()
3421
3422
    // Drag and Drop
3423
    IMGUI_API bool          IsDragDropActive();
3424
    IMGUI_API bool          BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);
3425
    IMGUI_API bool          BeginDragDropTargetViewport(ImGuiViewport* viewport, const ImRect* p_bb = NULL);
3426
    IMGUI_API void          ClearDragDrop();
3427
    IMGUI_API bool          IsDragDropPayloadBeingAccepted();
3428
    IMGUI_API void          RenderDragDropTargetRectForItem(const ImRect& bb);
3429
    IMGUI_API void          RenderDragDropTargetRectEx(ImDrawList* draw_list, const ImRect& bb);
3430
3431
    // Typing-Select API
3432
    // (provide Windows Explorer style "select items by typing partial name" + "cycle through items by typing same letter" feature)
3433
    // (this is currently not documented nor used by main library, but should work. See "widgets_typingselect" in imgui_test_suite for usage code. Please let us know if you use this!)
3434
    IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None);
3435
    IMGUI_API int           TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
3436
    IMGUI_API int           TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);
3437
    IMGUI_API int           TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);
3438
3439
    // Box-Select API
3440
    IMGUI_API bool          BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags);
3441
    IMGUI_API void          EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags);
3442
3443
    // Multi-Select API
3444
    IMGUI_API void          MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags);
3445
    IMGUI_API void          MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed);
3446
    IMGUI_API void          MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected);
3447
    IMGUI_API void          MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item);
3448
0
    inline ImGuiBoxSelectState*     GetBoxSelectState(ImGuiID id)   { ImGuiContext& g = *GImGui; return (id != 0 && g.BoxSelectState.ID == id && g.BoxSelectState.IsActive) ? &g.BoxSelectState : NULL; }
3449
0
    inline ImGuiMultiSelectState*   GetMultiSelectState(ImGuiID id) { ImGuiContext& g = *GImGui; return g.MultiSelectStorage.GetByKey(id); }
3450
3451
    // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)
3452
    IMGUI_API void          SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);
3453
    IMGUI_API void          BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns().
3454
    IMGUI_API void          EndColumns();                                                               // close columns
3455
    IMGUI_API void          PushColumnClipRect(int column_index);
3456
    IMGUI_API void          PushColumnsBackground();
3457
    IMGUI_API void          PopColumnsBackground();
3458
    IMGUI_API ImGuiID       GetColumnsID(const char* str_id, int count);
3459
    IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);
3460
    IMGUI_API float         GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm);
3461
    IMGUI_API float         GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset);
3462
3463
    // Tables: Candidates for public API
3464
    IMGUI_API void          TableOpenContextMenu(int column_n = -1);
3465
    IMGUI_API void          TableSetColumnWidth(int column_n, float width);
3466
    IMGUI_API void          TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);
3467
    IMGUI_API int           TableGetHoveredRow();       // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.
3468
    IMGUI_API float         TableGetHeaderRowHeight();
3469
    IMGUI_API float         TableGetHeaderAngledMaxLabelWidth();
3470
    IMGUI_API void          TablePushBackgroundChannel();
3471
    IMGUI_API void          TablePopBackgroundChannel();
3472
    IMGUI_API void          TablePushColumnChannel(int column_n);
3473
    IMGUI_API void          TablePopColumnChannel();
3474
    IMGUI_API void          TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count);
3475
3476
    // Tables: Internals
3477
0
    inline    ImGuiTable*   GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }
3478
    IMGUI_API ImGuiTable*   TableFindByID(ImGuiID id);
3479
    IMGUI_API bool          BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);
3480
    IMGUI_API void          TableBeginInitMemory(ImGuiTable* table, int columns_count);
3481
    IMGUI_API void          TableBeginApplyRequests(ImGuiTable* table);
3482
    IMGUI_API void          TableSetupDrawChannels(ImGuiTable* table);
3483
    IMGUI_API void          TableUpdateLayout(ImGuiTable* table);
3484
    IMGUI_API void          TableUpdateBorders(ImGuiTable* table);
3485
    IMGUI_API void          TableUpdateColumnsWeightFromWidth(ImGuiTable* table);
3486
    IMGUI_API void          TableDrawBorders(ImGuiTable* table);
3487
    IMGUI_API void          TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);
3488
    IMGUI_API bool          TableBeginContextMenuPopup(ImGuiTable* table);
3489
    IMGUI_API void          TableMergeDrawChannels(ImGuiTable* table);
3490
0
    inline ImGuiTableInstanceData*  TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }
3491
0
    inline ImGuiID                  TableGetInstanceID(ImGuiTable* table, int instance_no)   { return TableGetInstanceData(table, instance_no)->TableInstanceID; }
3492
    IMGUI_API void          TableSortSpecsSanitize(ImGuiTable* table);
3493
    IMGUI_API void          TableSortSpecsBuild(ImGuiTable* table);
3494
    IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);
3495
    IMGUI_API void          TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column);
3496
    IMGUI_API float         TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column);
3497
    IMGUI_API void          TableBeginRow(ImGuiTable* table);
3498
    IMGUI_API void          TableEndRow(ImGuiTable* table);
3499
    IMGUI_API void          TableBeginCell(ImGuiTable* table, int column_n);
3500
    IMGUI_API void          TableEndCell(ImGuiTable* table);
3501
    IMGUI_API ImRect        TableGetCellBgRect(const ImGuiTable* table, int column_n);
3502
    IMGUI_API const char*   TableGetColumnName(const ImGuiTable* table, int column_n);
3503
    IMGUI_API ImGuiID       TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0);
3504
    IMGUI_API float         TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n);
3505
    IMGUI_API void          TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n);
3506
    IMGUI_API void          TableSetColumnWidthAutoAll(ImGuiTable* table);
3507
    IMGUI_API void          TableRemove(ImGuiTable* table);
3508
    IMGUI_API void          TableGcCompactTransientBuffers(ImGuiTable* table);
3509
    IMGUI_API void          TableGcCompactTransientBuffers(ImGuiTableTempData* table);
3510
    IMGUI_API void          TableGcCompactSettings();
3511
3512
    // Tables: Settings
3513
    IMGUI_API void                  TableLoadSettings(ImGuiTable* table);
3514
    IMGUI_API void                  TableSaveSettings(ImGuiTable* table);
3515
    IMGUI_API void                  TableResetSettings(ImGuiTable* table);
3516
    IMGUI_API ImGuiTableSettings*   TableGetBoundSettings(ImGuiTable* table);
3517
    IMGUI_API void                  TableSettingsAddSettingsHandler();
3518
    IMGUI_API ImGuiTableSettings*   TableSettingsCreate(ImGuiID id, int columns_count);
3519
    IMGUI_API ImGuiTableSettings*   TableSettingsFindByID(ImGuiID id);
3520
3521
    // Tab Bars
3522
0
    inline    ImGuiTabBar*  GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; }
3523
    IMGUI_API ImGuiTabBar*  TabBarFindByID(ImGuiID id);
3524
    IMGUI_API void          TabBarRemove(ImGuiTabBar* tab_bar);
3525
    IMGUI_API bool          BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);
3526
    IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);
3527
    IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order);
3528
    IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar);
3529
0
    inline int              TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); }
3530
    IMGUI_API const char*   TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
3531
    IMGUI_API void          TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);
3532
    IMGUI_API void          TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
3533
    IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);
3534
    IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name);
3535
    IMGUI_API void          TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset);
3536
    IMGUI_API void          TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos);
3537
    IMGUI_API bool          TabBarProcessReorder(ImGuiTabBar* tab_bar);
3538
    IMGUI_API bool          TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window);
3539
    IMGUI_API void          TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width);
3540
    IMGUI_API ImVec2        TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker);
3541
    IMGUI_API ImVec2        TabItemCalcSize(ImGuiWindow* window);
3542
    IMGUI_API void          TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col);
3543
    IMGUI_API void          TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped);
3544
3545
    // Render helpers
3546
    // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.
3547
    // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)
3548
    IMGUI_API void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);
3549
    IMGUI_API void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
3550
    IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
3551
    IMGUI_API void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
3552
    IMGUI_API void          RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
3553
    IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
3554
    IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
3555
    IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);
3556
    IMGUI_API void          RenderNavCursor(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None); // Navigation highlight
3557
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3558
0
    inline    void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_None) { RenderNavCursor(bb, id, flags); } // Renamed in 1.91.4
3559
#endif
3560
    IMGUI_API const char*   FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.
3561
    IMGUI_API void          RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);
3562
3563
    // Render helpers (those functions don't access any ImGui state!)
3564
    IMGUI_API void          RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f);
3565
    IMGUI_API void          RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col);
3566
    IMGUI_API void          RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz);
3567
    IMGUI_API void          RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col);
3568
    IMGUI_API void          RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
3569
    IMGUI_API void          RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);
3570
3571
    // Widgets: Text
3572
    IMGUI_API void          TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);
3573
    IMGUI_API void          TextAligned(float align_x, float size_x, const char* fmt, ...);               // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
3574
    IMGUI_API void          TextAlignedV(float align_x, float size_x, const char* fmt, va_list args);
3575
3576
    // Widgets
3577
    IMGUI_API bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
3578
    IMGUI_API bool          ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);
3579
    IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);
3580
    IMGUI_API void          SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);
3581
    IMGUI_API void          SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);
3582
    IMGUI_API bool          CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);
3583
    IMGUI_API bool          CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value);
3584
3585
    // Widgets: Window Decorations
3586
    IMGUI_API bool          CloseButton(ImGuiID id, const ImVec2& pos);
3587
    IMGUI_API bool          CollapseButton(ImGuiID id, const ImVec2& pos);
3588
    IMGUI_API void          Scrollbar(ImGuiAxis axis);
3589
    IMGUI_API bool          ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags draw_rounding_flags = 0);
3590
    IMGUI_API ImRect        GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);
3591
    IMGUI_API ImGuiID       GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);
3592
    IMGUI_API ImGuiID       GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners
3593
    IMGUI_API ImGuiID       GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir);
3594
3595
    // Widgets low-level behaviors
3596
    IMGUI_API bool          ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);
3597
    IMGUI_API bool          DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags);
3598
    IMGUI_API bool          SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
3599
    IMGUI_API bool          SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);
3600
3601
    // Widgets: Tree Nodes
3602
    IMGUI_API bool          TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);
3603
    IMGUI_API void          TreeNodeDrawLineToChildNode(const ImVec2& target_pos);
3604
    IMGUI_API void          TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data);
3605
    IMGUI_API void          TreePushOverrideID(ImGuiID id);
3606
    IMGUI_API bool          TreeNodeGetOpen(ImGuiID storage_id);
3607
    IMGUI_API void          TreeNodeSetOpen(ImGuiID storage_id, bool open);
3608
    IMGUI_API bool          TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags);   // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.
3609
3610
    // Template functions are instantiated in imgui_widgets.cpp for a finite number of types.
3611
    // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).
3612
    // e.g. " extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); "
3613
    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
3614
    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API T     ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);
3615
    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);
3616
    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);
3617
    template<typename T>                                        IMGUI_API T     RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);
3618
    template<typename T>                                        IMGUI_API bool  CheckboxFlagsT(const char* label, T* flags, T flags_value);
3619
3620
    // Data type helpers
3621
    IMGUI_API const ImGuiDataTypeInfo*  DataTypeGetInfo(ImGuiDataType data_type);
3622
    IMGUI_API int           DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format);
3623
    IMGUI_API void          DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2);
3624
    IMGUI_API bool          DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty = NULL);
3625
    IMGUI_API int           DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2);
3626
    IMGUI_API bool          DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);
3627
    IMGUI_API bool          DataTypeIsZero(ImGuiDataType data_type, const void* p_data);
3628
3629
    // InputText
3630
    IMGUI_API bool          InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
3631
    IMGUI_API void          InputTextDeactivateHook(ImGuiID id);
3632
    IMGUI_API bool          TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags);
3633
    IMGUI_API bool          TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL);
3634
0
    inline bool             TempInputIsActive(ImGuiID id)       { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); }
3635
0
    inline ImGuiInputTextState* GetInputTextState(ImGuiID id)   { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active
3636
    IMGUI_API void          SetNextItemRefVal(ImGuiDataType data_type, void* p_data);
3637
0
    inline bool             IsItemActiveAsInputText() { ImGuiContext& g = *GImGui; return g.ActiveId != 0 && g.ActiveId == g.LastItemData.ID && g.InputTextState.ID == g.LastItemData.ID; } // This may be useful to apply workaround that a based on distinguish whenever an item is active as a text input field.
3638
3639
    // Color
3640
    IMGUI_API void          ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags);
3641
    IMGUI_API void          ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags);
3642
    IMGUI_API void          ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags);
3643
3644
    // Plot
3645
    IMGUI_API int           PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg);
3646
3647
    // Shade functions (write over already created vertices)
3648
    IMGUI_API void          ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);
3649
    IMGUI_API void          ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp);
3650
    IMGUI_API void          ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out);
3651
3652
    // Garbage collection
3653
    IMGUI_API void          GcCompactTransientMiscBuffers();
3654
    IMGUI_API void          GcCompactTransientWindowBuffers(ImGuiWindow* window);
3655
    IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);
3656
3657
    // Error handling, State Recovery
3658
    IMGUI_API bool          ErrorLog(const char* msg);
3659
    IMGUI_API void          ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out);
3660
    IMGUI_API void          ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in);
3661
    IMGUI_API void          ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in);
3662
    IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
3663
    IMGUI_API void          ErrorCheckEndFrameFinalizeErrorTooltip();
3664
    IMGUI_API bool          BeginErrorTooltip();
3665
    IMGUI_API void          EndErrorTooltip();
3666
3667
    // Debug Tools
3668
    IMGUI_API void          DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free
3669
    IMGUI_API void          DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255));
3670
    IMGUI_API void          DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255));
3671
    IMGUI_API void          DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255));
3672
    IMGUI_API void          DebugTextUnformattedWithLocateItem(const char* line_begin, const char* line_end);
3673
    IMGUI_API void          DebugLocateItem(ImGuiID target_id);                     // Call sparingly: only 1 at the same time!
3674
    IMGUI_API void          DebugLocateItemOnHover(ImGuiID target_id);              // Only call on reaction to a mouse Hover: because only 1 at the same time!
3675
    IMGUI_API void          DebugLocateItemResolveWithLastItem();
3676
    IMGUI_API void          DebugBreakClearData();
3677
    IMGUI_API bool          DebugBreakButton(const char* label, const char* description_of_location);
3678
    IMGUI_API void          DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location);
3679
    IMGUI_API void          ShowFontAtlas(ImFontAtlas* atlas);
3680
    IMGUI_API void          DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);
3681
    IMGUI_API void          DebugNodeColumns(ImGuiOldColumns* columns);
3682
    IMGUI_API void          DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);
3683
    IMGUI_API void          DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);
3684
    IMGUI_API void          DebugNodeFont(ImFont* font);
3685
    IMGUI_API void          DebugNodeFontGlyphesForSrcMask(ImFont* font, ImFontBaked* baked, int src_mask);
3686
    IMGUI_API void          DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);
3687
    IMGUI_API void          DebugNodeTexture(ImTextureData* tex, int int_id, const ImFontAtlasRect* highlight_rect = NULL); // ID used to facilitate persisting the "current" texture.
3688
    IMGUI_API void          DebugNodeStorage(ImGuiStorage* storage, const char* label);
3689
    IMGUI_API void          DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);
3690
    IMGUI_API void          DebugNodeTable(ImGuiTable* table);
3691
    IMGUI_API void          DebugNodeTableSettings(ImGuiTableSettings* settings);
3692
    IMGUI_API void          DebugNodeInputTextState(ImGuiInputTextState* state);
3693
    IMGUI_API void          DebugNodeTypingSelectState(ImGuiTypingSelectState* state);
3694
    IMGUI_API void          DebugNodeMultiSelectState(ImGuiMultiSelectState* state);
3695
    IMGUI_API void          DebugNodeWindow(ImGuiWindow* window, const char* label);
3696
    IMGUI_API void          DebugNodeWindowSettings(ImGuiWindowSettings* settings);
3697
    IMGUI_API void          DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);
3698
    IMGUI_API void          DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack);
3699
    IMGUI_API void          DebugNodeViewport(ImGuiViewportP* viewport);
3700
    IMGUI_API void          DebugRenderKeyboardPreview(ImDrawList* draw_list);
3701
    IMGUI_API void          DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb);
3702
3703
    // Obsolete functions
3704
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3705
    //inline void   SetItemUsingMouseWheel()                                            { SetItemKeyOwner(ImGuiKey_MouseWheelY); }      // Changed in 1.89
3706
    //inline bool   TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0)    { return TreeNodeUpdateNextOpen(id, flags); }   // Renamed in 1.89
3707
    //inline bool   IsKeyPressedMap(ImGuiKey key, bool repeat = true)                   { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!
3708
3709
    // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister():
3710
    //  (Old) IMGUI_VERSION_NUM  < 18209: using 'ItemAdd(....)'                              and 'bool tab_focused = FocusableItemRegister(...)'
3711
    //  (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)'  and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0'
3712
    //  (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)'     and 'bool tab_focused = (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))'
3713
    //inline bool   FocusableItemRegister(ImGuiWindow* window, ImGuiID id)              // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()
3714
    //inline void   FocusableItemUnregister(ImGuiWindow* window)                        // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem
3715
#endif
3716
3717
} // namespace ImGui
3718
3719
3720
//-----------------------------------------------------------------------------
3721
// [SECTION] ImFontLoader
3722
//-----------------------------------------------------------------------------
3723
3724
// Hooks and storage for a given font backend.
3725
// This structure is likely to evolve as we add support for incremental atlas updates.
3726
// Conceptually this could be public, but API is still going to be evolve.
3727
struct ImFontLoader
3728
{
3729
    const char*     Name;
3730
    bool            (*LoaderInit)(ImFontAtlas* atlas);
3731
    void            (*LoaderShutdown)(ImFontAtlas* atlas);
3732
    bool            (*FontSrcInit)(ImFontAtlas* atlas, ImFontConfig* src);
3733
    void            (*FontSrcDestroy)(ImFontAtlas* atlas, ImFontConfig* src);
3734
    bool            (*FontSrcContainsGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImWchar codepoint);
3735
    bool            (*FontBakedInit)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
3736
    void            (*FontBakedDestroy)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src);
3737
    bool            (*FontBakedLoadGlyph)(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src, ImWchar codepoint, ImFontGlyph* out_glyph, float* out_advance_x);
3738
3739
    // Size of backend data, Per Baked * Per Source. Buffers are managed by core to avoid excessive allocations.
3740
    // FIXME: At this point the two other types of buffers may be managed by core to be consistent?
3741
    size_t          FontBakedSrcLoaderDataSize;
3742
3743
1
    ImFontLoader()  { memset(this, 0, sizeof(*this)); }
3744
};
3745
3746
#ifdef IMGUI_ENABLE_STB_TRUETYPE
3747
IMGUI_API const ImFontLoader* ImFontAtlasGetFontLoaderForStbTruetype();
3748
#endif
3749
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
3750
typedef ImFontLoader ImFontBuilderIO; // [renamed/changed in 1.92] The types are not actually compatible but we provide this as a compile-time error report helper.
3751
#endif
3752
3753
//-----------------------------------------------------------------------------
3754
// [SECTION] ImFontAtlas internal API
3755
//-----------------------------------------------------------------------------
3756
3757
404k
#define IMGUI_FONT_SIZE_MAX                                     (512.0f)
3758
40
#define IMGUI_FONT_SIZE_THRESHOLD_FOR_LOADADVANCEXONLYMODE      (128.0f)
3759
3760
// Helpers: ImTextureRef ==/!= operators provided as convenience
3761
// (note that _TexID and _TexData are never set simultaneously)
3762
0
inline bool operator==(const ImTextureRef& lhs, const ImTextureRef& rhs)    { return lhs._TexID == rhs._TexID && lhs._TexData == rhs._TexData; }
3763
0
inline bool operator!=(const ImTextureRef& lhs, const ImTextureRef& rhs)    { return lhs._TexID != rhs._TexID || lhs._TexData != rhs._TexData; }
3764
3765
// Refer to ImFontAtlasPackGetRect() to better understand how this works.
3766
84
#define ImFontAtlasRectId_IndexMask_        (0x0007FFFF)    // 20-bits signed: index to access builder->RectsIndex[].
3767
64
#define ImFontAtlasRectId_GenerationMask_   (0x3FF00000)    // 10-bits: entry generation, so each ID is unique and get can safely detected old identifiers.
3768
86
#define ImFontAtlasRectId_GenerationShift_  (20)
3769
84
inline int               ImFontAtlasRectId_GetIndex(ImFontAtlasRectId id)       { return (id & ImFontAtlasRectId_IndexMask_); }
3770
64
inline unsigned int      ImFontAtlasRectId_GetGeneration(ImFontAtlasRectId id)  { return (unsigned int)(id & ImFontAtlasRectId_GenerationMask_) >> ImFontAtlasRectId_GenerationShift_; }
3771
22
inline ImFontAtlasRectId ImFontAtlasRectId_Make(int index_idx, int gen_idx)     { IM_ASSERT(index_idx >= 0 && index_idx <= ImFontAtlasRectId_IndexMask_ && gen_idx <= (ImFontAtlasRectId_GenerationMask_ >> ImFontAtlasRectId_GenerationShift_)); return (ImFontAtlasRectId)(index_idx | (gen_idx << ImFontAtlasRectId_GenerationShift_)); }
3772
3773
// Packed rectangle lookup entry (we need an indirection to allow removing/reordering rectangles)
3774
// User are returned ImFontAtlasRectId values which are meant to be persistent.
3775
// We handle this with an indirection. While Rects[] may be in theory shuffled, compacted etc., RectsIndex[] cannot it is keyed by ImFontAtlasRectId.
3776
// RectsIndex[] is used both as an index into Rects[] and an index into itself. This is basically a free-list. See ImFontAtlasBuildAllocRectIndexEntry() code.
3777
// Having this also makes it easier to e.g. sort rectangles during repack.
3778
struct ImFontAtlasRectEntry
3779
{
3780
    int                 TargetIndex : 20;   // When Used: ImFontAtlasRectId -> into Rects[]. When unused: index to next unused RectsIndex[] slot to consume free-list.
3781
    unsigned int        Generation : 10;    // Increased each time the entry is reused for a new rectangle.
3782
    unsigned int        IsUsed : 1;
3783
};
3784
3785
// Data available to potential texture post-processing functions
3786
struct ImFontAtlasPostProcessData
3787
{
3788
    ImFontAtlas*        FontAtlas;
3789
    ImFont*             Font;
3790
    ImFontConfig*       FontSrc;
3791
    ImFontBaked*        FontBaked;
3792
    ImFontGlyph*        Glyph;
3793
3794
    // Pixel data
3795
    void*               Pixels;
3796
    ImTextureFormat     Format;
3797
    int                 Pitch;
3798
    int                 Width;
3799
    int                 Height;
3800
};
3801
3802
// We avoid dragging imstb_rectpack.h into public header (partly because binding generators are having issues with it)
3803
#ifdef IMGUI_STB_NAMESPACE
3804
namespace IMGUI_STB_NAMESPACE { struct stbrp_node; }
3805
typedef IMGUI_STB_NAMESPACE::stbrp_node stbrp_node_im;
3806
#else
3807
struct stbrp_node;
3808
typedef stbrp_node stbrp_node_im;
3809
#endif
3810
struct stbrp_context_opaque { char data[80]; };
3811
3812
// Internal storage for incrementally packing and building a ImFontAtlas
3813
struct ImFontAtlasBuilder
3814
{
3815
    stbrp_context_opaque        PackContext;            // Actually 'stbrp_context' but we don't want to define this in the header file.
3816
    ImVector<stbrp_node_im>     PackNodes;
3817
    ImVector<ImTextureRect>     Rects;
3818
    ImVector<ImFontAtlasRectEntry> RectsIndex;          // ImFontAtlasRectId -> index into Rects[]
3819
    ImVector<unsigned char>     TempBuffer;             // Misc scratch buffer
3820
    int                         RectsIndexFreeListStart;// First unused entry
3821
    int                         RectsPackedCount;       // Number of packed rectangles.
3822
    int                         RectsPackedSurface;     // Number of packed pixels. Used when compacting to heuristically find the ideal texture size.
3823
    int                         RectsDiscardedCount;
3824
    int                         RectsDiscardedSurface;
3825
    int                         FrameCount;             // Current frame count
3826
    ImVec2i                     MaxRectSize;            // Largest rectangle to pack (de-facto used as a "minimum texture size")
3827
    ImVec2i                     MaxRectBounds;          // Bottom-right most used pixels
3828
    bool                        LockDisableResize;      // Disable resizing texture
3829
    bool                        PreloadedAllGlyphsRanges; // Set when missing ImGuiBackendFlags_RendererHasTextures features forces atlas to preload everything.
3830
3831
    // Cache of all ImFontBaked
3832
    ImStableVector<ImFontBaked,32> BakedPool;
3833
    ImGuiStorage                BakedMap;               // BakedId --> ImFontBaked*
3834
    int                         BakedDiscardedCount;
3835
3836
    // Custom rectangle identifiers
3837
    ImFontAtlasRectId           PackIdMouseCursors;     // White pixel + mouse cursors. Also happen to be fallback in case of packing failure.
3838
    ImFontAtlasRectId           PackIdLinesTexData;
3839
3840
1
    ImFontAtlasBuilder()        { memset(this, 0, sizeof(*this)); FrameCount = -1; RectsIndexFreeListStart = -1; PackIdMouseCursors = PackIdLinesTexData = -1; }
3841
};
3842
3843
IMGUI_API void              ImFontAtlasBuildInit(ImFontAtlas* atlas);
3844
IMGUI_API void              ImFontAtlasBuildDestroy(ImFontAtlas* atlas);
3845
IMGUI_API void              ImFontAtlasBuildMain(ImFontAtlas* atlas);
3846
IMGUI_API void              ImFontAtlasBuildSetupFontLoader(ImFontAtlas* atlas, const ImFontLoader* font_loader);
3847
IMGUI_API void              ImFontAtlasBuildUpdatePointers(ImFontAtlas* atlas);
3848
IMGUI_API void              ImFontAtlasBuildRenderBitmapFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char);
3849
IMGUI_API void              ImFontAtlasBuildClear(ImFontAtlas* atlas); // Clear output and custom rects
3850
3851
IMGUI_API ImTextureData*    ImFontAtlasTextureAdd(ImFontAtlas* atlas, int w, int h);
3852
IMGUI_API void              ImFontAtlasTextureMakeSpace(ImFontAtlas* atlas);
3853
IMGUI_API void              ImFontAtlasTextureRepack(ImFontAtlas* atlas, int w, int h);
3854
IMGUI_API void              ImFontAtlasTextureGrow(ImFontAtlas* atlas, int old_w = -1, int old_h = -1);
3855
IMGUI_API void              ImFontAtlasTextureCompact(ImFontAtlas* atlas);
3856
IMGUI_API ImVec2i           ImFontAtlasTextureGetSizeEstimate(ImFontAtlas* atlas);
3857
3858
IMGUI_API void              ImFontAtlasBuildSetupFontSpecialGlyphs(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
3859
IMGUI_API void              ImFontAtlasBuildLegacyPreloadAllGlyphRanges(ImFontAtlas* atlas); // Legacy
3860
IMGUI_API void              ImFontAtlasBuildGetOversampleFactors(ImFontConfig* src, ImFontBaked* baked, int* out_oversample_h, int* out_oversample_v);
3861
IMGUI_API void              ImFontAtlasBuildDiscardBakes(ImFontAtlas* atlas, int unused_frames);
3862
3863
IMGUI_API bool              ImFontAtlasFontSourceInit(ImFontAtlas* atlas, ImFontConfig* src);
3864
IMGUI_API void              ImFontAtlasFontSourceAddToFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* src);
3865
IMGUI_API void              ImFontAtlasFontDestroySourceData(ImFontAtlas* atlas, ImFontConfig* src);
3866
IMGUI_API bool              ImFontAtlasFontInitOutput(ImFontAtlas* atlas, ImFont* font); // Using FontDestroyOutput/FontInitOutput sequence useful notably if font loader params have changed
3867
IMGUI_API void              ImFontAtlasFontDestroyOutput(ImFontAtlas* atlas, ImFont* font);
3868
IMGUI_API void              ImFontAtlasFontDiscardBakes(ImFontAtlas* atlas, ImFont* font, int unused_frames);
3869
3870
IMGUI_API ImGuiID           ImFontAtlasBakedGetId(ImGuiID font_id, float baked_size, float rasterizer_density);
3871
IMGUI_API ImFontBaked*      ImFontAtlasBakedGetOrAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
3872
IMGUI_API ImFontBaked*      ImFontAtlasBakedGetClosestMatch(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density);
3873
IMGUI_API ImFontBaked*      ImFontAtlasBakedAdd(ImFontAtlas* atlas, ImFont* font, float font_size, float font_rasterizer_density, ImGuiID baked_id);
3874
IMGUI_API void              ImFontAtlasBakedDiscard(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked);
3875
IMGUI_API ImFontGlyph*      ImFontAtlasBakedAddFontGlyph(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, const ImFontGlyph* in_glyph);
3876
IMGUI_API void              ImFontAtlasBakedAddFontGlyphAdvancedX(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImWchar codepoint, float advance_x);
3877
IMGUI_API void              ImFontAtlasBakedDiscardFontGlyph(ImFontAtlas* atlas, ImFont* font, ImFontBaked* baked, ImFontGlyph* glyph);
3878
IMGUI_API void              ImFontAtlasBakedSetFontGlyphBitmap(ImFontAtlas* atlas, ImFontBaked* baked, ImFontConfig* src, ImFontGlyph* glyph, ImTextureRect* r, const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch);
3879
3880
IMGUI_API void              ImFontAtlasPackInit(ImFontAtlas* atlas);
3881
IMGUI_API ImFontAtlasRectId ImFontAtlasPackAddRect(ImFontAtlas* atlas, int w, int h, ImFontAtlasRectEntry* overwrite_entry = NULL);
3882
IMGUI_API ImTextureRect*    ImFontAtlasPackGetRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
3883
IMGUI_API ImTextureRect*    ImFontAtlasPackGetRectSafe(ImFontAtlas* atlas, ImFontAtlasRectId id);
3884
IMGUI_API void              ImFontAtlasPackDiscardRect(ImFontAtlas* atlas, ImFontAtlasRectId id);
3885
3886
IMGUI_API void              ImFontAtlasUpdateNewFrame(ImFontAtlas* atlas, int frame_count, bool renderer_has_textures);
3887
IMGUI_API void              ImFontAtlasAddDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
3888
IMGUI_API void              ImFontAtlasRemoveDrawListSharedData(ImFontAtlas* atlas, ImDrawListSharedData* data);
3889
IMGUI_API void              ImFontAtlasUpdateDrawListsTextures(ImFontAtlas* atlas, ImTextureRef old_tex, ImTextureRef new_tex);
3890
IMGUI_API void              ImFontAtlasUpdateDrawListsSharedData(ImFontAtlas* atlas);
3891
3892
IMGUI_API void              ImFontAtlasTextureBlockConvert(const unsigned char* src_pixels, ImTextureFormat src_fmt, int src_pitch, unsigned char* dst_pixels, ImTextureFormat dst_fmt, int dst_pitch, int w, int h);
3893
IMGUI_API void              ImFontAtlasTextureBlockPostProcess(ImFontAtlasPostProcessData* data);
3894
IMGUI_API void              ImFontAtlasTextureBlockPostProcessMultiply(ImFontAtlasPostProcessData* data, float multiply_factor);
3895
IMGUI_API void              ImFontAtlasTextureBlockFill(ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h, ImU32 col);
3896
IMGUI_API void              ImFontAtlasTextureBlockCopy(ImTextureData* src_tex, int src_x, int src_y, ImTextureData* dst_tex, int dst_x, int dst_y, int w, int h);
3897
IMGUI_API void              ImFontAtlasTextureBlockQueueUpload(ImFontAtlas* atlas, ImTextureData* tex, int x, int y, int w, int h);
3898
3899
IMGUI_API int               ImTextureDataGetFormatBytesPerPixel(ImTextureFormat format);
3900
IMGUI_API const char*       ImTextureDataGetStatusName(ImTextureStatus status);
3901
IMGUI_API const char*       ImTextureDataGetFormatName(ImTextureFormat format);
3902
3903
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
3904
IMGUI_API void              ImFontAtlasDebugLogTextureRequests(ImFontAtlas* atlas);
3905
#endif
3906
3907
IMGUI_API bool      ImFontAtlasGetMouseCursorTexData(ImFontAtlas* atlas, ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);
3908
3909
//-----------------------------------------------------------------------------
3910
// [SECTION] Test Engine specific hooks (imgui_test_engine)
3911
//-----------------------------------------------------------------------------
3912
3913
#ifdef IMGUI_ENABLE_TEST_ENGINE
3914
extern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);           // item_data may be NULL
3915
extern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);
3916
extern void         ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);
3917
extern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);
3918
3919
// In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data);
3920
#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA)    // Register item bounding box
3921
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS)    // Register item label and status flags (optional)
3922
#define IMGUI_TEST_ENGINE_LOG(_FMT,...)                     ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__)                                      // Custom log entry from user land into test log
3923
#else
3924
#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA)      ((void)0)
3925
487k
#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      ((void)g)
3926
#endif
3927
3928
//-----------------------------------------------------------------------------
3929
3930
#if defined(__clang__)
3931
#pragma clang diagnostic pop
3932
#elif defined(__GNUC__)
3933
#pragma GCC diagnostic pop
3934
#endif
3935
3936
#ifdef _MSC_VER
3937
#pragma warning (pop)
3938
#endif
3939
3940
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui_tables.cpp
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (tables and columns code)
3
4
/*
5
6
Index of this file:
7
8
// [SECTION] Commentary
9
// [SECTION] Header mess
10
// [SECTION] Tables: Main code
11
// [SECTION] Tables: Simple accessors
12
// [SECTION] Tables: Row changes
13
// [SECTION] Tables: Columns changes
14
// [SECTION] Tables: Columns width management
15
// [SECTION] Tables: Drawing
16
// [SECTION] Tables: Sorting
17
// [SECTION] Tables: Headers
18
// [SECTION] Tables: Context Menu
19
// [SECTION] Tables: Settings (.ini data)
20
// [SECTION] Tables: Garbage Collection
21
// [SECTION] Tables: Debugging
22
// [SECTION] Columns, BeginColumns, EndColumns, etc.
23
24
*/
25
26
// Navigating this file:
27
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
28
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
29
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
30
31
//-----------------------------------------------------------------------------
32
// [SECTION] Commentary
33
//-----------------------------------------------------------------------------
34
35
//-----------------------------------------------------------------------------
36
// Typical tables call flow: (root level is generally public API):
37
//-----------------------------------------------------------------------------
38
// - BeginTable()                               user begin into a table
39
//    | BeginChild()                            - (if ScrollX/ScrollY is set)
40
//    | TableBeginInitMemory()                  - first time table is used
41
//    | TableResetSettings()                    - on settings reset
42
//    | TableLoadSettings()                     - on settings load
43
//    | TableBeginApplyRequests()               - apply queued resizing/reordering/hiding requests
44
//    | - TableSetColumnWidth()                 - apply resizing width (for mouse resize, often requested by previous frame)
45
//    |    - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width
46
// - TableSetupColumn()                         user submit columns details (optional)
47
// - TableSetupScrollFreeze()                   user submit scroll freeze information (optional)
48
//-----------------------------------------------------------------------------
49
// - TableUpdateLayout() [Internal]             followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().
50
//    | TableSetupDrawChannels()                - setup ImDrawList channels
51
//    | TableUpdateBorders()                    - detect hovering columns for resize, ahead of contents submission
52
//    | TableBeginContextMenuPopup()
53
//    | - TableDrawDefaultContextMenu()         - draw right-click context menu contents
54
//-----------------------------------------------------------------------------
55
// - TableHeadersRow() or TableHeader()         user submit a headers row (optional)
56
//    | TableSortSpecsClickColumn()             - when left-clicked: alter sort order and sort direction
57
//    | TableOpenContextMenu()                  - when right-clicked: trigger opening of the default context menu
58
// - TableGetSortSpecs()                        user queries updated sort specs (optional, generally after submitting headers)
59
// - TableNextRow()                             user begin into a new row (also automatically called by TableHeadersRow())
60
//    | TableEndRow()                           - finish existing row
61
//    | TableBeginRow()                         - add a new row
62
// - TableSetColumnIndex() / TableNextColumn()  user begin into a cell
63
//    | TableEndCell()                          - close existing column/cell
64
//    | TableBeginCell()                        - enter into current column/cell
65
// - [...]                                      user emit contents
66
//-----------------------------------------------------------------------------
67
// - EndTable()                                 user ends the table
68
//    | TableDrawBorders()                      - draw outer borders, inner vertical borders
69
//    | TableMergeDrawChannels()                - merge draw channels if clipping isn't required
70
//    | EndChild()                              - (if ScrollX/ScrollY is set)
71
//-----------------------------------------------------------------------------
72
73
//-----------------------------------------------------------------------------
74
// TABLE SIZING
75
//-----------------------------------------------------------------------------
76
// (Read carefully because this is subtle but it does make sense!)
77
//-----------------------------------------------------------------------------
78
// About 'outer_size':
79
// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags.
80
// Default value is ImVec2(0.0f, 0.0f).
81
//   X
82
//   - outer_size.x <= 0.0f  ->  Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge.
83
//   - outer_size.x  > 0.0f  ->  Set Fixed width.
84
//   Y with ScrollX/ScrollY disabled: we output table directly in current window
85
//   - outer_size.y  < 0.0f  ->  Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll.
86
//   - outer_size.y  = 0.0f  ->  No minimum height (but will auto extend, unless _NoHostExtendY is set)
87
//   - outer_size.y  > 0.0f  ->  Set Minimum height (but will auto extend, unless _NoHostExtendY is set)
88
//   Y with ScrollX/ScrollY enabled: using a child window for scrolling
89
//   - outer_size.y  < 0.0f  ->  Bottom-align. Not meaningful if parent window can vertically scroll.
90
//   - outer_size.y  = 0.0f  ->  Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window.
91
//   - outer_size.y  > 0.0f  ->  Set Exact height. Recommended when using Scrolling on any axis.
92
//-----------------------------------------------------------------------------
93
// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags.
94
// Important to note how the two flags have slightly different behaviors!
95
//   - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.
96
//   - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible.
97
// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height.
98
// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable).
99
//-----------------------------------------------------------------------------
100
// About 'inner_width':
101
//   With ScrollX disabled:
102
//   - inner_width          ->  *ignored*
103
//   With ScrollX enabled:
104
//   - inner_width  < 0.0f  ->  *illegal* fit in known width (right align from outer_size.x) <-- weird
105
//   - inner_width  = 0.0f  ->  fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.
106
//   - inner_width  > 0.0f  ->  override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!
107
//-----------------------------------------------------------------------------
108
// Details:
109
// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept
110
//   of "available space" doesn't make sense.
111
// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding
112
//   of what the value does.
113
//-----------------------------------------------------------------------------
114
115
//-----------------------------------------------------------------------------
116
// COLUMNS SIZING POLICIES
117
// (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags)
118
//-----------------------------------------------------------------------------
119
// About overriding column sizing policy and width/weight with TableSetupColumn():
120
// We use a default parameter of -1 for 'init_width'/'init_weight'.
121
//   - with ImGuiTableColumnFlags_WidthFixed,    init_width  <= 0 (default)  --> width is automatic
122
//   - with ImGuiTableColumnFlags_WidthFixed,    init_width  >  0 (explicit) --> width is custom
123
//   - with ImGuiTableColumnFlags_WidthStretch,  init_weight <= 0 (default)  --> weight is 1.0f
124
//   - with ImGuiTableColumnFlags_WidthStretch,  init_weight >  0 (explicit) --> weight is custom
125
// Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f)
126
// and you can fit a 100.0f wide item in it without clipping and with padding honored.
127
//-----------------------------------------------------------------------------
128
// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag)
129
//   - with Table policy ImGuiTableFlags_SizingFixedFit      --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width
130
//   - with Table policy ImGuiTableFlags_SizingFixedSame     --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width
131
//   - with Table policy ImGuiTableFlags_SizingStretchSame   --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f
132
//   - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents
133
// Default Width and default Weight can be overridden when calling TableSetupColumn().
134
//-----------------------------------------------------------------------------
135
// About mixing Fixed/Auto and Stretch columns together:
136
//   - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.
137
//   - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place!
138
//     that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in.
139
//   - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents.
140
//   - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths.
141
//-----------------------------------------------------------------------------
142
// About using column width:
143
// If a column is manually resizable or has a width specified with TableSetupColumn():
144
//   - you may use GetContentRegionAvail().x to query the width available in a given column.
145
//   - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width.
146
// If the column is not resizable and has no width specified with TableSetupColumn():
147
//   - its width will be automatic and be set to the max of items submitted.
148
//   - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN).
149
//   - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).
150
//-----------------------------------------------------------------------------
151
152
153
//-----------------------------------------------------------------------------
154
// TABLES CLIPPING/CULLING
155
//-----------------------------------------------------------------------------
156
// About clipping/culling of Rows in Tables:
157
// - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows.
158
//   ImGuiListClipper is reliant on the fact that rows are of equal height.
159
//   See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper.
160
// - Note that auto-resizing columns don't play well with using the clipper.
161
//   By default a table with _ScrollX but without _Resizable will have column auto-resize.
162
//   So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed.
163
//-----------------------------------------------------------------------------
164
// About clipping/culling of Columns in Tables:
165
// - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing
166
//   width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know
167
//   it is not going to contribute to row height.
168
//   In many situations, you may skip submitting contents for every column but one (e.g. the first one).
169
// - Case A: column is not hidden by user, and at least partially in sight (most common case).
170
// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.
171
// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).
172
//
173
//                        [A]         [B]          [C]
174
//  TableNextColumn():    true        false        false       -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height.
175
//          SkipItems:    false       false        true        -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output.
176
//           ClipRect:    normal      zero-width   zero-width  -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.
177
//  ImDrawList output:    normal      dummy        dummy       -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).
178
//
179
// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.
180
//   However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.
181
//-----------------------------------------------------------------------------
182
// About clipping/culling of whole Tables:
183
// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false.
184
//-----------------------------------------------------------------------------
185
186
//-----------------------------------------------------------------------------
187
// [SECTION] Header mess
188
//-----------------------------------------------------------------------------
189
190
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
191
#define _CRT_SECURE_NO_WARNINGS
192
#endif
193
194
#ifndef IMGUI_DEFINE_MATH_OPERATORS
195
#define IMGUI_DEFINE_MATH_OPERATORS
196
#endif
197
198
#include "imgui.h"
199
#ifndef IMGUI_DISABLE
200
#include "imgui_internal.h"
201
202
// System includes
203
#include <stdint.h>     // intptr_t
204
205
// Visual Studio warnings
206
#ifdef _MSC_VER
207
#pragma warning (disable: 4127)     // condition expression is constant
208
#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
209
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
210
#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types
211
#endif
212
#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
213
#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
214
#endif
215
216
// Clang/GCC warnings with -Weverything
217
#if defined(__clang__)
218
#if __has_warning("-Wunknown-warning-option")
219
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
220
#endif
221
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
222
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
223
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
224
#pragma clang diagnostic ignored "-Wformat"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'
225
#pragma clang diagnostic ignored "-Wformat-nonliteral"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
226
#pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
227
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
228
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
229
#pragma clang diagnostic ignored "-Wenum-enum-conversion"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
230
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
231
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
232
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
233
#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
234
#pragma clang diagnostic ignored "-Wswitch-default"                 // warning: 'switch' missing 'default' label
235
#elif defined(__GNUC__)
236
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
237
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
238
#pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
239
#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
240
#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
241
#pragma GCC diagnostic ignored "-Wstrict-overflow"
242
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
243
#endif
244
245
//-----------------------------------------------------------------------------
246
// [SECTION] Tables: Main code
247
//-----------------------------------------------------------------------------
248
// - TableFixFlags() [Internal]
249
// - TableFindByID() [Internal]
250
// - BeginTable()
251
// - BeginTableEx() [Internal]
252
// - TableBeginInitMemory() [Internal]
253
// - TableBeginApplyRequests() [Internal]
254
// - TableSetupColumnFlags() [Internal]
255
// - TableUpdateLayout() [Internal]
256
// - TableUpdateBorders() [Internal]
257
// - EndTable()
258
// - TableSetupColumn()
259
// - TableSetupScrollFreeze()
260
//-----------------------------------------------------------------------------
261
262
// Configuration
263
static const int TABLE_DRAW_CHANNEL_BG0 = 0;
264
static const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1;
265
static const int TABLE_DRAW_CHANNEL_NOCLIP = 2;                     // When using ImGuiTableFlags_NoClip (this becomes the last visible channel)
266
static const float TABLE_BORDER_SIZE                     = 1.0f;    // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering.
267
static const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f;    // Extend outside inner borders.
268
static const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f;   // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped.
269
270
// Helper
271
inline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window)
272
0
{
273
    // Adjust flags: set default sizing policy
274
0
    if ((flags & ImGuiTableFlags_SizingMask_) == 0)
275
0
        flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame;
276
277
    // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame
278
0
    if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
279
0
        flags |= ImGuiTableFlags_NoKeepColumnsVisible;
280
281
    // Adjust flags: enforce borders when resizable
282
0
    if (flags & ImGuiTableFlags_Resizable)
283
0
        flags |= ImGuiTableFlags_BordersInnerV;
284
285
    // Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on
286
0
    if (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY))
287
0
        flags &= ~(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY);
288
289
    // Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody
290
0
    if (flags & ImGuiTableFlags_NoBordersInBodyUntilResize)
291
0
        flags &= ~ImGuiTableFlags_NoBordersInBody;
292
293
    // Adjust flags: disable saved settings if there's nothing to save
294
0
    if ((flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0)
295
0
        flags |= ImGuiTableFlags_NoSavedSettings;
296
297
    // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set)
298
0
    if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings)
299
0
        flags |= ImGuiTableFlags_NoSavedSettings;
300
301
0
    return flags;
302
0
}
303
304
ImGuiTable* ImGui::TableFindByID(ImGuiID id)
305
0
{
306
0
    ImGuiContext& g = *GImGui;
307
0
    return g.Tables.GetByKey(id);
308
0
}
309
310
// Read about "TABLE SIZING" at the top of this file.
311
bool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)
312
0
{
313
0
    ImGuiID id = GetID(str_id);
314
0
    return BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width);
315
0
}
316
317
bool    ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)
318
0
{
319
0
    ImGuiContext& g = *GImGui;
320
0
    ImGuiWindow* outer_window = GetCurrentWindow();
321
0
    if (outer_window->SkipItems) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible.
322
0
        return false;
323
324
    // Sanity checks
325
0
    IM_ASSERT(columns_count > 0 && columns_count < IMGUI_TABLE_MAX_COLUMNS);
326
0
    if (flags & ImGuiTableFlags_ScrollX)
327
0
        IM_ASSERT(inner_width >= 0.0f);
328
329
    // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.
330
    // FIXME: coarse clipping because access to table data causes two issues:
331
    // - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.
332
    // - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.
333
    // The side-effects of accessing table data on coarse clip would be:
334
    // - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.
335
    // - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
336
0
    const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
337
0
    const ImVec2 avail_size = GetContentRegionAvail();
338
0
    const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
339
0
    const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
340
0
    const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
341
0
    if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
342
0
    {
343
0
        ItemSize(outer_rect);
344
0
        ItemAdd(outer_rect, id);
345
0
        g.NextWindowData.ClearFlags();
346
0
        return false;
347
0
    }
348
349
    // [DEBUG] Debug break requested by user
350
0
    if (g.DebugBreakInTable == id)
351
0
        IM_DEBUG_BREAK();
352
353
    // Acquire storage for the table
354
0
    ImGuiTable* table = g.Tables.GetOrAddByKey(id);
355
356
    // Acquire temporary buffers
357
0
    const int table_idx = g.Tables.GetIndex(table);
358
0
    if (++g.TablesTempDataStacked > g.TablesTempData.Size)
359
0
        g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData());
360
0
    ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1];
361
0
    temp_data->TableIndex = table_idx;
362
0
    table->DrawSplitter = &table->TempData->DrawSplitter;
363
0
    table->DrawSplitter->Clear();
364
365
    // Fix flags
366
0
    table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0;
367
0
    flags = TableFixFlags(flags, outer_window);
368
369
    // Initialize
370
0
    const int previous_frame_active = table->LastFrameActive;
371
0
    const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
372
0
    const ImGuiTableFlags previous_flags = table->Flags;
373
0
    table->ID = id;
374
0
    table->Flags = flags;
375
0
    table->LastFrameActive = g.FrameCount;
376
0
    table->OuterWindow = table->InnerWindow = outer_window;
377
0
    table->ColumnsCount = columns_count;
378
0
    table->IsLayoutLocked = false;
379
0
    table->InnerWidth = inner_width;
380
0
    table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
381
0
    temp_data->UserOuterSize = outer_size;
382
383
    // Instance data (for instance 0, TableID == TableInstanceID)
384
0
    ImGuiID instance_id;
385
0
    table->InstanceCurrent = (ImS16)instance_no;
386
0
    if (instance_no > 0)
387
0
    {
388
0
        IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID");
389
0
        if (table->InstanceDataExtra.Size < instance_no)
390
0
            table->InstanceDataExtra.push_back(ImGuiTableInstanceData());
391
0
        instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instances" followed by (int)instance_no in ID stack.
392
0
    }
393
0
    else
394
0
    {
395
0
        instance_id = id;
396
0
    }
397
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
398
0
    table_instance->TableInstanceID = instance_id;
399
400
    // When not using a child window, WorkRect.Max will grow as we append contents.
401
0
    if (use_child_window)
402
0
    {
403
        // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent
404
        // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)
405
0
        ImVec2 override_content_size(FLT_MAX, FLT_MAX);
406
0
        if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY))
407
0
            override_content_size.y = FLT_MIN;
408
409
        // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and
410
        // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align
411
        // based on the right side of the child window work rect, which would require knowing ahead if we are going to
412
        // have decoration taking horizontal spaces (typically a vertical scrollbar).
413
0
        if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f)
414
0
            override_content_size.x = inner_width;
415
416
0
        if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX)
417
0
            SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f));
418
419
        // Reset scroll if we are reactivating it
420
0
        if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
421
0
            if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)
422
0
                SetNextWindowScroll(ImVec2(0.0f, 0.0f));
423
424
        // Create scrolling region (without border and zero window padding)
425
0
        ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
426
0
        ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;
427
0
        if (flags & ImGuiTableFlags_ScrollX)
428
0
            child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;
429
0
        BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);
430
0
        table->InnerWindow = g.CurrentWindow;
431
0
        table->WorkRect = table->InnerWindow->WorkRect;
432
0
        table->OuterRect = table->InnerWindow->Rect();
433
0
        table->InnerRect = table->InnerWindow->InnerRect;
434
0
        IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f);
435
436
        // Allow submitting when host is measuring
437
0
        if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)
438
0
            table->InnerWindow->SkipItems = false;
439
440
        // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned
441
0
        if (instance_no == 0)
442
0
        {
443
0
            table->HasScrollbarYPrev = table->HasScrollbarYCurr;
444
0
            table->HasScrollbarYCurr = false;
445
0
        }
446
0
        table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY;
447
0
    }
448
0
    else
449
0
    {
450
        // For non-scrolling tables, WorkRect == OuterRect == InnerRect.
451
        // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
452
0
        table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
453
0
        table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
454
0
        table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
455
0
    }
456
457
    // Push a standardized ID for both child-using and not-child-using tables
458
0
    PushOverrideID(id);
459
0
    if (instance_no > 0)
460
0
        PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol.
461
462
    // Backup a copy of host window members we will modify
463
0
    ImGuiWindow* inner_window = table->InnerWindow;
464
0
    table->HostIndentX = inner_window->DC.Indent.x;
465
0
    table->HostClipRect = inner_window->ClipRect;
466
0
    table->HostSkipItems = inner_window->SkipItems;
467
0
    temp_data->HostBackupWorkRect = inner_window->WorkRect;
468
0
    temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;
469
0
    temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;
470
0
    temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize;
471
0
    temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize;
472
0
    temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos;
473
0
    temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth;
474
0
    temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
475
0
    inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
476
477
    // Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
478
    // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
479
    // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
480
    // problem only affect scrolling tables in this case we can get away with doing it without extra cost).
481
0
    if (inner_window != outer_window)
482
0
    {
483
        // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
484
        // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
485
        // different x/y values to BeginChild().
486
0
        if (flags & ImGuiTableFlags_BordersOuterV)
487
0
        {
488
0
            table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
489
0
            if (inner_window->DecoOuterSizeX2 == 0.0f)
490
0
                table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
491
0
        }
492
0
        if (flags & ImGuiTableFlags_BordersOuterH)
493
0
        {
494
0
            table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
495
0
            if (inner_window->DecoOuterSizeY2 == 0.0f)
496
0
                table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
497
0
        }
498
0
    }
499
500
    // Padding and Spacing
501
    // - None               ........Content..... Pad .....Content........
502
    // - PadOuter           | Pad ..Content..... Pad .....Content.. Pad |
503
    // - PadInner           ........Content.. Pad | Pad ..Content........
504
    // - PadOuter+PadInner  | Pad ..Content.. Pad | Pad ..Content.. Pad |
505
0
    const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0;
506
0
    const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true;
507
0
    const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f;
508
0
    const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f;
509
0
    const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f;
510
0
    table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border;
511
0
    table->CellSpacingX2 = inner_spacing_explicit;
512
0
    table->CellPaddingX = inner_padding_explicit;
513
514
0
    const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
515
0
    const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f;
516
0
    table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX;
517
518
0
    table->CurrentColumn = -1;
519
0
    table->CurrentRow = -1;
520
0
    table->RowBgColorCounter = 0;
521
0
    table->LastRowFlags = ImGuiTableRowFlags_None;
522
0
    table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
523
0
    table->InnerClipRect.ClipWith(table->WorkRect);     // We need this to honor inner_width
524
0
    table->InnerClipRect.ClipWithFull(table->HostClipRect);
525
0
    table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;
526
527
0
    table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
528
0
    table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
529
0
    table->RowCellPaddingY = 0.0f;
530
0
    table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any
531
0
    table->FreezeColumnsRequest = table->FreezeColumnsCount = 0;
532
0
    table->IsUnfrozenRows = true;
533
0
    table->DeclColumnsCount = table->AngledHeadersCount = 0;
534
0
    if (previous_frame_active + 1 < g.FrameCount)
535
0
        table->IsActiveIdInTable = false;
536
0
    table->AngledHeadersHeight = 0.0f;
537
0
    temp_data->AngledHeadersExtraWidth = 0.0f;
538
539
    // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
540
0
    table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);
541
0
    table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight);
542
543
    // Make table current
544
0
    g.CurrentTable = table;
545
0
    inner_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
546
0
    outer_window->DC.CurrentTableIdx = table_idx;
547
0
    if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
548
0
        inner_window->DC.CurrentTableIdx = table_idx;
549
550
0
    if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
551
0
        table->IsResetDisplayOrderRequest = true;
552
553
    // Mark as used to avoid GC
554
0
    if (table_idx >= g.TablesLastTimeActive.Size)
555
0
        g.TablesLastTimeActive.resize(table_idx + 1, -1.0f);
556
0
    g.TablesLastTimeActive[table_idx] = (float)g.Time;
557
0
    temp_data->LastTimeActive = (float)g.Time;
558
0
    table->MemoryCompacted = false;
559
560
    // Setup memory buffer (clear data if columns count changed)
561
0
    ImGuiTableColumn* old_columns_to_preserve = NULL;
562
0
    void* old_columns_raw_data = NULL;
563
0
    const int old_columns_count = table->Columns.size();
564
0
    if (old_columns_count != 0 && old_columns_count != columns_count)
565
0
    {
566
        // Attempt to preserve width on column count change (#4046)
567
0
        old_columns_to_preserve = table->Columns.Data;
568
0
        old_columns_raw_data = table->RawData;
569
0
        table->RawData = NULL;
570
0
    }
571
0
    if (table->RawData == NULL)
572
0
    {
573
0
        TableBeginInitMemory(table, columns_count);
574
0
        table->IsInitializing = table->IsSettingsRequestLoad = true;
575
0
    }
576
0
    if (table->IsResetAllRequest)
577
0
        TableResetSettings(table);
578
0
    if (table->IsInitializing)
579
0
    {
580
        // Initialize
581
0
        table->SettingsOffset = -1;
582
0
        table->IsSortSpecsDirty = true;
583
0
        table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
584
0
        table->InstanceInteracted = -1;
585
0
        table->ContextPopupColumn = -1;
586
0
        table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;
587
0
        table->AutoFitSingleColumn = -1;
588
0
        table->HoveredColumnBody = table->HoveredColumnBorder = -1;
589
0
        for (int n = 0; n < columns_count; n++)
590
0
        {
591
0
            ImGuiTableColumn* column = &table->Columns[n];
592
0
            if (old_columns_to_preserve && n < old_columns_count)
593
0
            {
594
                // FIXME: We don't attempt to preserve column order in this path.
595
0
                *column = old_columns_to_preserve[n];
596
0
            }
597
0
            else
598
0
            {
599
0
                float width_auto = column->WidthAuto;
600
0
                *column = ImGuiTableColumn();
601
0
                column->WidthAuto = width_auto;
602
0
                column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker
603
0
                column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true;
604
0
            }
605
0
            column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n;
606
0
        }
607
0
    }
608
0
    if (old_columns_raw_data)
609
0
        IM_FREE(old_columns_raw_data);
610
611
    // Load settings
612
0
    if (table->IsSettingsRequestLoad)
613
0
        TableLoadSettings(table);
614
615
    // Handle DPI/font resize
616
    // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
617
    // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
618
    // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
619
    // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
620
0
    const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?
621
0
    if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)
622
0
    {
623
0
        const float scale_factor = new_ref_scale_unit / table->RefScale;
624
        //IMGUI_DEBUG_PRINT("[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\n", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);
625
0
        for (int n = 0; n < columns_count; n++)
626
0
            table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;
627
0
    }
628
0
    table->RefScale = new_ref_scale_unit;
629
630
    // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..
631
    // This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user.
632
    // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
633
0
    inner_window->SkipItems = true;
634
635
    // Clear names
636
    // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()
637
0
    if (table->ColumnsNames.Buf.Size > 0)
638
0
        table->ColumnsNames.Buf.resize(0);
639
640
    // Apply queued resizing/reordering/hiding requests
641
0
    TableBeginApplyRequests(table);
642
643
0
    return true;
644
0
}
645
646
// For reference, the average total _allocation count_ for a table is:
647
// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables[])
648
// + 1 (for table->RawData allocated below)
649
// + 1 (for table->ColumnsNames, if names are used)
650
// Shared allocations for the maximum number of simultaneously nested tables (generally a very small number)
651
// + 1 (for table->Splitter._Channels)
652
// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)
653
// Where active_channels_count is variable but often == columns_count or == columns_count + 1, see TableSetupDrawChannels() for details.
654
// Unused channels don't perform their +2 allocations.
655
void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)
656
0
{
657
    // Allocate single buffer for our arrays
658
0
    const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count);
659
0
    ImSpanAllocator<6> span_allocator;
660
0
    span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn));
661
0
    span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx));
662
0
    span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4);
663
0
    for (int n = 3; n < 6; n++)
664
0
        span_allocator.Reserve(n, columns_bit_array_size);
665
0
    table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes());
666
0
    memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes());
667
0
    span_allocator.SetArenaBasePtr(table->RawData);
668
0
    span_allocator.GetSpan(0, &table->Columns);
669
0
    span_allocator.GetSpan(1, &table->DisplayOrderToIndex);
670
0
    span_allocator.GetSpan(2, &table->RowCellData);
671
0
    table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3);
672
0
    table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4);
673
0
    table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5);
674
0
}
675
676
// Apply queued resizing/reordering/hiding requests
677
void ImGui::TableBeginApplyRequests(ImGuiTable* table)
678
0
{
679
    // Handle resizing request
680
    // (We process this in the TableBegin() of the first instance of each table)
681
    // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?
682
0
    if (table->InstanceCurrent == 0)
683
0
    {
684
0
        if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX)
685
0
            TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth);
686
0
        table->LastResizedColumn = table->ResizedColumn;
687
0
        table->ResizedColumnNextWidth = FLT_MAX;
688
0
        table->ResizedColumn = -1;
689
690
        // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy.
691
        // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings.
692
0
        if (table->AutoFitSingleColumn != -1)
693
0
        {
694
0
            TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto);
695
0
            table->AutoFitSingleColumn = -1;
696
0
        }
697
0
    }
698
699
    // Handle reordering request
700
    // Note: we don't clear ReorderColumn after handling the request.
701
0
    if (table->InstanceCurrent == 0)
702
0
    {
703
0
        if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1)
704
0
            table->ReorderColumn = -1;
705
0
        table->HeldHeaderColumn = -1;
706
0
        if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0)
707
0
        {
708
            // We need to handle reordering across hidden columns.
709
            // In the configuration below, moving C to the right of E will lead to:
710
            //    ... C [D] E  --->  ... [D] E  C   (Column name/index)
711
            //    ... 2  3  4        ...  2  3  4   (Display order)
712
0
            const int reorder_dir = table->ReorderColumnDir;
713
0
            IM_ASSERT(reorder_dir == -1 || reorder_dir == +1);
714
0
            IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable);
715
0
            ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn];
716
0
            ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn];
717
0
            IM_UNUSED(dst_column);
718
0
            const int src_order = src_column->DisplayOrder;
719
0
            const int dst_order = dst_column->DisplayOrder;
720
0
            src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order;
721
0
            for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir)
722
0
                table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir;
723
0
            IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir);
724
725
            // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former.
726
0
            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
727
0
                table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;
728
0
            table->ReorderColumnDir = 0;
729
0
            table->IsSettingsDirty = true;
730
0
        }
731
0
    }
732
733
    // Handle display order reset request
734
0
    if (table->IsResetDisplayOrderRequest)
735
0
    {
736
0
        for (int n = 0; n < table->ColumnsCount; n++)
737
0
            table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n;
738
0
        table->IsResetDisplayOrderRequest = false;
739
0
        table->IsSettingsDirty = true;
740
0
    }
741
0
}
742
743
// Adjust flags: default width mode + stretch columns are not allowed when auto extending
744
static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in)
745
0
{
746
0
    ImGuiTableColumnFlags flags = flags_in;
747
748
    // Sizing Policy
749
0
    if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0)
750
0
    {
751
0
        const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);
752
0
        if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame)
753
0
            flags |= ImGuiTableColumnFlags_WidthFixed;
754
0
        else
755
0
            flags |= ImGuiTableColumnFlags_WidthStretch;
756
0
    }
757
0
    else
758
0
    {
759
0
        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used.
760
0
    }
761
762
    // Resize
763
0
    if ((table->Flags & ImGuiTableFlags_Resizable) == 0)
764
0
        flags |= ImGuiTableColumnFlags_NoResize;
765
766
    // Sorting
767
0
    if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending))
768
0
        flags |= ImGuiTableColumnFlags_NoSort;
769
770
    // Indentation
771
0
    if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0)
772
0
        flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable;
773
774
    // Alignment
775
    //if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0)
776
    //    flags |= ImGuiTableColumnFlags_AlignCenter;
777
    //IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used.
778
779
    // Preserve status flags
780
0
    column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_);
781
782
    // Build an ordered list of available sort directions
783
0
    column->SortDirectionsAvailCount = column->SortDirectionsAvailMask = column->SortDirectionsAvailList = 0;
784
0
    if (table->Flags & ImGuiTableFlags_Sortable)
785
0
    {
786
0
        int count = 0, mask = 0, list = 0;
787
0
        if ((flags & ImGuiTableColumnFlags_PreferSortAscending)  != 0 && (flags & ImGuiTableColumnFlags_NoSortAscending)  == 0) { mask |= 1 << ImGuiSortDirection_Ascending;  list |= ImGuiSortDirection_Ascending  << (count << 1); count++; }
788
0
        if ((flags & ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; }
789
0
        if ((flags & ImGuiTableColumnFlags_PreferSortAscending)  == 0 && (flags & ImGuiTableColumnFlags_NoSortAscending)  == 0) { mask |= 1 << ImGuiSortDirection_Ascending;  list |= ImGuiSortDirection_Ascending  << (count << 1); count++; }
790
0
        if ((flags & ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; }
791
0
        if ((table->Flags & ImGuiTableFlags_SortTristate) || count == 0) { mask |= 1 << ImGuiSortDirection_None; count++; }
792
0
        column->SortDirectionsAvailList = (ImU8)list;
793
0
        column->SortDirectionsAvailMask = (ImU8)mask;
794
0
        column->SortDirectionsAvailCount = (ImU8)count;
795
0
        ImGui::TableFixColumnSortDirection(table, column);
796
0
    }
797
0
}
798
799
// Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function.
800
// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first.
801
// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns.
802
// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns?
803
void ImGui::TableUpdateLayout(ImGuiTable* table)
804
0
{
805
0
    ImGuiContext& g = *GImGui;
806
0
    IM_ASSERT(table->IsLayoutLocked == false);
807
808
0
    const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);
809
0
    table->IsDefaultDisplayOrder = true;
810
0
    table->ColumnsEnabledCount = 0;
811
0
    ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);
812
0
    ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);
813
0
    table->LeftMostEnabledColumn = -1;
814
0
    table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE
815
816
    // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.
817
    // Process columns in their visible orders as we are building the Prev/Next indices.
818
0
    int count_fixed = 0;                // Number of columns that have fixed sizing policies
819
0
    int count_stretch = 0;              // Number of columns that have stretch sizing policies
820
0
    int prev_visible_column_idx = -1;
821
0
    bool has_auto_fit_request = false;
822
0
    bool has_resizable = false;
823
0
    float stretch_sum_width_auto = 0.0f;
824
0
    float fixed_max_width_auto = 0.0f;
825
0
    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
826
0
    {
827
0
        const int column_n = table->DisplayOrderToIndex[order_n];
828
0
        if (column_n != order_n)
829
0
            table->IsDefaultDisplayOrder = false;
830
0
        ImGuiTableColumn* column = &table->Columns[column_n];
831
832
        // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.
833
        // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway.
834
        // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect.
835
0
        if (table->DeclColumnsCount <= column_n)
836
0
        {
837
0
            TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);
838
0
            column->NameOffset = -1;
839
0
            column->UserID = 0;
840
0
            column->InitStretchWeightOrWidth = -1.0f;
841
0
        }
842
843
        // Update Enabled state, mark settings and sort specs dirty
844
0
        if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide))
845
0
            column->IsUserEnabledNextFrame = true;
846
0
        if (column->IsUserEnabled != column->IsUserEnabledNextFrame)
847
0
        {
848
0
            column->IsUserEnabled = column->IsUserEnabledNextFrame;
849
0
            table->IsSettingsDirty = true;
850
0
        }
851
0
        column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0;
852
853
0
        if (column->SortOrder != -1 && !column->IsEnabled)
854
0
            table->IsSortSpecsDirty = true;
855
0
        if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti))
856
0
            table->IsSortSpecsDirty = true;
857
858
        // Auto-fit unsized columns
859
0
        const bool start_auto_fit = (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f);
860
0
        if (start_auto_fit)
861
0
            column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames
862
863
0
        if (!column->IsEnabled)
864
0
        {
865
0
            column->IndexWithinEnabledSet = -1;
866
0
            continue;
867
0
        }
868
869
        // Mark as enabled and link to previous/next enabled column
870
0
        column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;
871
0
        column->NextEnabledColumn = -1;
872
0
        if (prev_visible_column_idx != -1)
873
0
            table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n;
874
0
        else
875
0
            table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n;
876
0
        column->IndexWithinEnabledSet = table->ColumnsEnabledCount++;
877
0
        ImBitArraySetBit(table->EnabledMaskByIndex, column_n);
878
0
        ImBitArraySetBit(table->EnabledMaskByDisplayOrder, column->DisplayOrder);
879
0
        prev_visible_column_idx = column_n;
880
0
        IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);
881
882
        // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
883
        // Combine width from regular rows + width from headers unless requested not to.
884
0
        if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
885
0
            column->WidthAuto = TableGetColumnWidthAuto(table, column);
886
887
        // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
888
0
        const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0;
889
0
        if (column_is_resizable)
890
0
            has_resizable = true;
891
0
        if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable)
892
0
            column->WidthAuto = column->InitStretchWeightOrWidth;
893
894
0
        if (column->AutoFitQueue != 0x00)
895
0
            has_auto_fit_request = true;
896
0
        if (column->Flags & ImGuiTableColumnFlags_WidthStretch)
897
0
        {
898
0
            stretch_sum_width_auto += column->WidthAuto;
899
0
            count_stretch++;
900
0
        }
901
0
        else
902
0
        {
903
0
            fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto);
904
0
            count_fixed++;
905
0
        }
906
0
    }
907
0
    if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))
908
0
        table->IsSortSpecsDirty = true;
909
0
    table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;
910
0
    IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0);
911
912
    // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid
913
    // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510.
914
    // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time.
915
0
    if (has_auto_fit_request && table->OuterWindow != table->InnerWindow)
916
0
        table->InnerWindow->SkipItems = false;
917
0
    if (has_auto_fit_request)
918
0
        table->IsSettingsDirty = true;
919
920
    // [Part 3] Fix column flags and record a few extra information.
921
0
    float sum_width_requests = 0.0f;    // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding.
922
0
    float stretch_sum_weights = 0.0f;   // Sum of all weights for stretch columns.
923
0
    table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;
924
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
925
0
    {
926
0
        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
927
0
            continue;
928
0
        ImGuiTableColumn* column = &table->Columns[column_n];
929
930
0
        const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0;
931
0
        if (column->Flags & ImGuiTableColumnFlags_WidthFixed)
932
0
        {
933
            // Apply same widths policy
934
0
            float width_auto = column->WidthAuto;
935
0
            if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable))
936
0
                width_auto = fixed_max_width_auto;
937
938
            // Apply automatic width
939
            // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)
940
0
            if (column->AutoFitQueue != 0x00)
941
0
                column->WidthRequest = width_auto;
942
0
            else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && column->IsRequestOutput)
943
0
                column->WidthRequest = width_auto;
944
945
            // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets
946
            // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very
947
            // large height (= first frame scrollbar display very off + clipper would skip lots of items).
948
            // This is merely making the side-effect less extreme, but doesn't properly fixes it.
949
            // FIXME: Move this to ->WidthGiven to avoid temporary lossyless?
950
            // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.
951
0
            if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto)
952
0
                column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale?
953
0
            sum_width_requests += column->WidthRequest;
954
0
        }
955
0
        else
956
0
        {
957
            // Initialize stretch weight
958
0
            if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable)
959
0
            {
960
0
                if (column->InitStretchWeightOrWidth > 0.0f)
961
0
                    column->StretchWeight = column->InitStretchWeightOrWidth;
962
0
                else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp)
963
0
                    column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch;
964
0
                else
965
0
                    column->StretchWeight = 1.0f;
966
0
            }
967
968
0
            stretch_sum_weights += column->StretchWeight;
969
0
            if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder)
970
0
                table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n;
971
0
            if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder)
972
0
                table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n;
973
0
        }
974
0
        column->IsPreserveWidthAuto = false;
975
0
        sum_width_requests += table->CellPaddingX * 2.0f;
976
0
    }
977
0
    table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed;
978
0
    table->ColumnsStretchSumWeights = stretch_sum_weights;
979
980
    // [Part 4] Apply final widths based on requested widths
981
0
    const ImRect work_rect = table->WorkRect;
982
0
    const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
983
0
    const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
984
0
    const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);
985
0
    const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
986
0
    float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
987
0
    table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;
988
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
989
0
    {
990
0
        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
991
0
            continue;
992
0
        ImGuiTableColumn* column = &table->Columns[column_n];
993
994
        // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)
995
0
        if (column->Flags & ImGuiTableColumnFlags_WidthStretch)
996
0
        {
997
0
            float weight_ratio = column->StretchWeight / stretch_sum_weights;
998
0
            column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f);
999
0
            width_remaining_for_stretched_columns -= column->WidthRequest;
1000
0
        }
1001
1002
        // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column
1003
        // See additional comments in TableSetColumnWidth().
1004
0
        if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1)
1005
0
            column->Flags |= ImGuiTableColumnFlags_NoDirectResize_;
1006
1007
        // Assign final width, record width in case we will need to shrink
1008
0
        column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth));
1009
0
        table->ColumnsGivenWidth += column->WidthGiven;
1010
0
    }
1011
1012
    // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column).
1013
    // Using right-to-left distribution (more likely to match resizing cursor).
1014
0
    if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))
1015
0
        for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)
1016
0
        {
1017
0
            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
1018
0
                continue;
1019
0
            ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];
1020
0
            if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))
1021
0
                continue;
1022
0
            column->WidthRequest += 1.0f;
1023
0
            column->WidthGiven += 1.0f;
1024
0
            width_remaining_for_stretched_columns -= 1.0f;
1025
0
        }
1026
1027
    // Determine if table is hovered which will be used to flag columns as hovered.
1028
    // - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
1029
    //   but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily
1030
    //   clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).
1031
    // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.
1032
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
1033
0
    table_instance->HoveredRowLast = table_instance->HoveredRowNext;
1034
0
    table_instance->HoveredRowNext = -1;
1035
0
    table->HoveredColumnBody = table->HoveredColumnBorder = -1;
1036
0
    const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));
1037
0
    const ImGuiID backup_active_id = g.ActiveId;
1038
0
    g.ActiveId = 0;
1039
0
    const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None);
1040
0
    g.ActiveId = backup_active_id;
1041
1042
    // Determine skewed MousePos.x to support angled headers.
1043
0
    float mouse_skewed_x = g.IO.MousePos.x;
1044
0
    if (table->AngledHeadersHeight > 0.0f)
1045
0
        if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight)
1046
0
            mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope);
1047
1048
    // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column
1049
    // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.
1050
0
    int visible_n = 0;
1051
0
    bool has_at_least_one_column_requesting_output = false;
1052
0
    bool offset_x_frozen = (table->FreezeColumnsCount > 0);
1053
0
    float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;
1054
0
    ImRect host_clip_rect = table->InnerClipRect;
1055
    //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;
1056
0
    ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount);
1057
0
    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
1058
0
    {
1059
0
        const int column_n = table->DisplayOrderToIndex[order_n];
1060
0
        ImGuiTableColumn* column = &table->Columns[column_n];
1061
1062
        // Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
1063
0
        column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);
1064
1065
0
        if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
1066
0
        {
1067
0
            offset_x += work_rect.Min.x - table->OuterRect.Min.x;
1068
0
            offset_x_frozen = false;
1069
0
        }
1070
1071
        // Clear status flags
1072
0
        column->Flags &= ~ImGuiTableColumnFlags_StatusMask_;
1073
1074
0
        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
1075
0
        {
1076
            // Hidden column: clear a few fields and we are done with it for the remainder of the function.
1077
            // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.
1078
0
            column->MinX = column->MaxX = column->WorkMinX = column->ClipRect.Min.x = column->ClipRect.Max.x = offset_x;
1079
0
            column->WidthGiven = 0.0f;
1080
0
            column->ClipRect.Min.y = work_rect.Min.y;
1081
0
            column->ClipRect.Max.y = FLT_MAX;
1082
0
            column->ClipRect.ClipWithFull(host_clip_rect);
1083
0
            column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false;
1084
0
            column->IsSkipItems = true;
1085
0
            column->ItemWidth = 1.0f;
1086
0
            continue;
1087
0
        }
1088
1089
        // Lock start position
1090
0
        column->MinX = offset_x;
1091
1092
        // Lock width based on start position and minimum/maximum width for this position
1093
0
        column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
1094
0
        column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
1095
0
        column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
1096
0
        column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
1097
1098
        // Lock other positions
1099
        // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging.
1100
        // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.
1101
        // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.
1102
        // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.
1103
0
        const float previous_instance_work_min_x = column->WorkMinX;
1104
0
        column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;
1105
0
        column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max
1106
0
        column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);
1107
0
        column->ClipRect.Min.x = column->MinX;
1108
0
        column->ClipRect.Min.y = work_rect.Min.y;
1109
0
        column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX;
1110
0
        column->ClipRect.Max.y = FLT_MAX;
1111
0
        column->ClipRect.ClipWithFull(host_clip_rect);
1112
1113
        // Mark column as Clipped (not in sight)
1114
        // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.
1115
        // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY.
1116
        // Taking advantage of LastOuterHeight would yield good results there...
1117
        // FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x,
1118
        // and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else).
1119
        // Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small.
1120
0
        column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x);
1121
0
        column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);
1122
0
        const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;
1123
0
        if (is_visible)
1124
0
            ImBitArraySetBit(table->VisibleMaskByIndex, column_n);
1125
1126
        // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.
1127
0
        column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;
1128
1129
        // Mark column as SkipItems (ignoring all items/layout)
1130
        // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2)
1131
0
        column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;
1132
0
        if (column->IsSkipItems)
1133
0
            IM_ASSERT(!is_visible);
1134
0
        if (column->IsRequestOutput && !column->IsSkipItems)
1135
0
            has_at_least_one_column_requesting_output = true;
1136
1137
        // Update status flags
1138
0
        column->Flags |= ImGuiTableColumnFlags_IsEnabled;
1139
0
        if (is_visible)
1140
0
            column->Flags |= ImGuiTableColumnFlags_IsVisible;
1141
0
        if (column->SortOrder != -1)
1142
0
            column->Flags |= ImGuiTableColumnFlags_IsSorted;
1143
1144
        // Detect hovered column
1145
0
        if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
1146
0
        {
1147
0
            column->Flags |= ImGuiTableColumnFlags_IsHovered;
1148
0
            table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
1149
0
        }
1150
1151
        // Alignment
1152
        // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
1153
        // many cases (to be able to honor this we might be able to store a log of cells width, per row, for
1154
        // visible rows, but nav/programmatic scroll would have visible artifacts.)
1155
        //if (column->Flags & ImGuiTableColumnFlags_AlignRight)
1156
        //    column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen);
1157
        //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter)
1158
        //    column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
1159
1160
        // Reset content width variables
1161
0
        if (table->InstanceCurrent == 0)
1162
0
        {
1163
0
            column->ContentMaxXFrozen = column->WorkMinX;
1164
0
            column->ContentMaxXUnfrozen = column->WorkMinX;
1165
0
            column->ContentMaxXHeadersUsed = column->WorkMinX;
1166
0
            column->ContentMaxXHeadersIdeal = column->WorkMinX;
1167
0
        }
1168
0
        else
1169
0
        {
1170
            // As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.
1171
0
            const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x;
1172
0
            column->ContentMaxXFrozen += offset_from_previous_instance;
1173
0
            column->ContentMaxXUnfrozen += offset_from_previous_instance;
1174
0
            column->ContentMaxXHeadersUsed += offset_from_previous_instance;
1175
0
            column->ContentMaxXHeadersIdeal += offset_from_previous_instance;
1176
0
        }
1177
1178
        // Don't decrement auto-fit counters until container window got a chance to submit its items
1179
0
        if (table->HostSkipItems == false && table->InstanceCurrent == 0)
1180
0
        {
1181
0
            column->AutoFitQueue >>= 1;
1182
0
            column->CannotSkipItemsQueue >>= 1;
1183
0
        }
1184
1185
0
        if (visible_n < table->FreezeColumnsCount)
1186
0
            host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x);
1187
1188
0
        offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
1189
0
        visible_n++;
1190
0
    }
1191
1192
    // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.
1193
    // Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar.
1194
0
    if (has_at_least_one_column_requesting_output == false)
1195
0
    {
1196
0
        table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;
1197
0
        table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false;
1198
0
    }
1199
1200
    // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)
1201
    // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either
1202
    // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.
1203
0
    const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);
1204
0
    if (is_hovering_table && table->HoveredColumnBody == -1)
1205
0
        if (mouse_skewed_x >= unused_x1)
1206
0
            table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;
1207
0
    if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))
1208
0
        table->Flags &= ~ImGuiTableFlags_Resizable;
1209
1210
0
    table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0);
1211
1212
    // [Part 8] Lock actual OuterRect/WorkRect right-most position.
1213
    // This is done late to handle the case of fixed-columns tables not claiming more widths that they need.
1214
    // Because of this we are careful with uses of WorkRect and InnerClipRect before this point.
1215
0
    if (table->RightMostStretchedColumn != -1)
1216
0
        table->Flags &= ~ImGuiTableFlags_NoHostExtendX;
1217
0
    if (table->Flags & ImGuiTableFlags_NoHostExtendX)
1218
0
    {
1219
0
        table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1;
1220
0
        table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1);
1221
0
    }
1222
0
    table->InnerWindow->ParentWorkRect = table->WorkRect;
1223
0
    table->BorderX1 = table->InnerClipRect.Min.x;
1224
0
    table->BorderX2 = table->InnerClipRect.Max.x;
1225
1226
    // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call.
1227
0
    float window_content_max_y;
1228
0
    if (table->Flags & ImGuiTableFlags_NoHostExtendY)
1229
0
        window_content_max_y = table->OuterRect.Max.y;
1230
0
    else
1231
0
        window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y);
1232
0
    table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y);
1233
1234
    // [Part 9] Allocate draw channels and setup background cliprect
1235
0
    TableSetupDrawChannels(table);
1236
1237
    // [Part 10] Hit testing on borders
1238
0
    if (table->Flags & ImGuiTableFlags_Resizable)
1239
0
        TableUpdateBorders(table);
1240
0
    table_instance->LastTopHeadersRowHeight = 0.0f;
1241
0
    table->IsLayoutLocked = true;
1242
0
    table->IsUsingHeaders = false;
1243
1244
    // Highlight header
1245
0
    table->HighlightColumnHeader = -1;
1246
0
    if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent)
1247
0
        table->HighlightColumnHeader = table->ContextPopupColumn;
1248
0
    else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1)
1249
0
        if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))
1250
0
            table->HighlightColumnHeader = table->HoveredColumnBody;
1251
1252
    // [Part 11] Default context menu
1253
    // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
1254
    // - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
1255
    // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
1256
    //   e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
1257
0
    if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
1258
0
    {
1259
0
        TableDrawDefaultContextMenu(table, table->Flags);
1260
0
        EndPopup();
1261
0
    }
1262
1263
    // [Part 12] Sanitize and build sort specs before we have a chance to use them for display.
1264
    // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)
1265
0
    if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable))
1266
0
        TableSortSpecsBuild(table);
1267
1268
    // [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns)
1269
0
    if (table->FreezeColumnsRequest > 0)
1270
0
        table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x;
1271
0
    if (table->FreezeRowsRequest > 0)
1272
0
        table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight;
1273
0
    table_instance->LastFrozenHeight = 0.0f;
1274
1275
    // Initial state
1276
0
    ImGuiWindow* inner_window = table->InnerWindow;
1277
0
    if (table->Flags & ImGuiTableFlags_NoClip)
1278
0
        table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
1279
0
    else
1280
0
        inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
1281
0
}
1282
1283
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
1284
// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise.
1285
void ImGui::TableUpdateBorders(ImGuiTable* table)
1286
0
{
1287
0
    ImGuiContext& g = *GImGui;
1288
0
    IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable);
1289
1290
    // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and
1291
    // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not
1292
    // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
1293
    // Actual columns highlight/render will be performed in EndTable() and not be affected.
1294
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
1295
0
    const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale);
1296
0
    const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;
1297
0
    const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight);
1298
0
    const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;
1299
1300
0
    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
1301
0
    {
1302
0
        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
1303
0
            continue;
1304
1305
0
        const int column_n = table->DisplayOrderToIndex[order_n];
1306
0
        ImGuiTableColumn* column = &table->Columns[column_n];
1307
0
        if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_))
1308
0
            continue;
1309
1310
        // ImGuiTableFlags_NoBordersInBodyUntilResize will be honored in TableDrawBorders()
1311
0
        const float border_y2_hit = (table->Flags & ImGuiTableFlags_NoBordersInBody) ? hit_y2_head : hit_y2_body;
1312
0
        if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false)
1313
0
            continue;
1314
1315
0
        if (!column->IsVisibleX && table->LastResizedColumn != column_n)
1316
0
            continue;
1317
1318
0
        ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);
1319
0
        ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit);
1320
0
        ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav);
1321
        //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));
1322
1323
0
        bool hovered = false, held = false;
1324
0
        bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);
1325
0
        if (pressed && IsMouseDoubleClicked(0))
1326
0
        {
1327
0
            TableSetColumnWidthAutoSingle(table, column_n);
1328
0
            ClearActiveID();
1329
0
            held = false;
1330
0
        }
1331
0
        if (held)
1332
0
        {
1333
0
            if (table->LastResizedColumn == -1)
1334
0
                table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX;
1335
0
            table->ResizedColumn = (ImGuiTableColumnIdx)column_n;
1336
0
            table->InstanceInteracted = table->InstanceCurrent;
1337
0
        }
1338
0
        if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held)
1339
0
        {
1340
0
            table->HoveredColumnBorder = (ImGuiTableColumnIdx)column_n;
1341
0
            SetMouseCursor(ImGuiMouseCursor_ResizeEW);
1342
0
        }
1343
0
    }
1344
0
}
1345
1346
void    ImGui::EndTable()
1347
0
{
1348
0
    ImGuiContext& g = *GImGui;
1349
0
    ImGuiTable* table = g.CurrentTable;
1350
0
    if (table == NULL)
1351
0
    {
1352
0
        IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!");
1353
0
        return;
1354
0
    }
1355
1356
    // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
1357
    // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
1358
    //IM_ASSERT(table->IsLayoutLocked && "Table unused: never called TableNextRow(), is that the intent?");
1359
1360
    // If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our
1361
    // code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc.
1362
0
    if (!table->IsLayoutLocked)
1363
0
        TableUpdateLayout(table);
1364
1365
0
    const ImGuiTableFlags flags = table->Flags;
1366
0
    ImGuiWindow* inner_window = table->InnerWindow;
1367
0
    ImGuiWindow* outer_window = table->OuterWindow;
1368
0
    ImGuiTableTempData* temp_data = table->TempData;
1369
0
    IM_ASSERT(inner_window == g.CurrentWindow);
1370
0
    IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);
1371
1372
0
    if (table->IsInsideRow)
1373
0
        TableEndRow(table);
1374
1375
    // Context menu in columns body
1376
0
    if (flags & ImGuiTableFlags_ContextMenuInBody)
1377
0
        if (table->HoveredColumnBody != -1 && !IsAnyItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))
1378
0
            TableOpenContextMenu((int)table->HoveredColumnBody);
1379
1380
    // Finalize table height
1381
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
1382
0
    inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize;
1383
0
    inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize;
1384
0
    inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos;
1385
0
    const float inner_content_max_y = table->RowPosY2;
1386
0
    IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y);
1387
0
    if (inner_window != outer_window)
1388
0
        inner_window->DC.CursorMaxPos.y = inner_content_max_y;
1389
0
    else if (!(flags & ImGuiTableFlags_NoHostExtendY))
1390
0
        table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height
1391
0
    table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y);
1392
0
    table_instance->LastOuterHeight = table->OuterRect.GetHeight();
1393
1394
    // Setup inner scrolling range
1395
    // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
1396
    // but since the later is likely to be impossible to do we'd rather update both axes together.
1397
0
    if (table->Flags & ImGuiTableFlags_ScrollX)
1398
0
    {
1399
0
        const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
1400
0
        float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x;
1401
0
        if (table->RightMostEnabledColumn != -1)
1402
0
            max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border);
1403
0
        if (table->ResizedColumn != -1)
1404
0
            max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);
1405
0
        table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth;
1406
0
    }
1407
1408
    // Pop clipping rect
1409
0
    if (!(flags & ImGuiTableFlags_NoClip))
1410
0
        inner_window->DrawList->PopClipRect();
1411
0
    inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back();
1412
1413
    // Draw borders
1414
0
    if ((flags & ImGuiTableFlags_Borders) != 0)
1415
0
        TableDrawBorders(table);
1416
1417
#if 0
1418
    // Strip out dummy channel draw calls
1419
    // We have no way to prevent user submitting direct ImDrawList calls into a hidden column (but ImGui:: calls will be clipped out)
1420
    // Pros: remove draw calls which will have no effect. since they'll have zero-size cliprect they may be early out anyway.
1421
    // Cons: making it harder for users watching metrics/debugger to spot the wasted vertices.
1422
    if (table->DummyDrawChannel != (ImGuiTableColumnIdx)-1)
1423
    {
1424
        ImDrawChannel* dummy_channel = &table->DrawSplitter._Channels[table->DummyDrawChannel];
1425
        dummy_channel->_CmdBuffer.resize(0);
1426
        dummy_channel->_IdxBuffer.resize(0);
1427
    }
1428
#endif
1429
1430
    // Flatten channels and merge draw calls
1431
0
    ImDrawListSplitter* splitter = table->DrawSplitter;
1432
0
    splitter->SetCurrentChannel(inner_window->DrawList, 0);
1433
0
    if ((table->Flags & ImGuiTableFlags_NoClip) == 0)
1434
0
        TableMergeDrawChannels(table);
1435
0
    splitter->Merge(inner_window->DrawList);
1436
1437
    // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()
1438
0
    float auto_fit_width_for_fixed = 0.0f;
1439
0
    float auto_fit_width_for_stretched = 0.0f;
1440
0
    float auto_fit_width_for_stretched_min = 0.0f;
1441
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
1442
0
        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
1443
0
        {
1444
0
            ImGuiTableColumn* column = &table->Columns[column_n];
1445
0
            float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column);
1446
0
            if (column->Flags & ImGuiTableColumnFlags_WidthFixed)
1447
0
                auto_fit_width_for_fixed += column_width_request;
1448
0
            else
1449
0
                auto_fit_width_for_stretched += column_width_request;
1450
0
            if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0)
1451
0
                auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights));
1452
0
        }
1453
0
    const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
1454
0
    table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min);
1455
1456
    // Update scroll
1457
0
    if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window)
1458
0
    {
1459
0
        inner_window->Scroll.x = 0.0f;
1460
0
    }
1461
0
    else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent)
1462
0
    {
1463
        // When releasing a column being resized, scroll to keep the resulting column in sight
1464
0
        const float neighbor_width_to_keep_visible = table->MinColumnWidth + table->CellPaddingX * 2.0f;
1465
0
        ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn];
1466
0
        if (column->MaxX < table->InnerClipRect.Min.x)
1467
0
            SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x - neighbor_width_to_keep_visible, 1.0f);
1468
0
        else if (column->MaxX > table->InnerClipRect.Max.x)
1469
0
            SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x + neighbor_width_to_keep_visible, 1.0f);
1470
0
    }
1471
1472
    // Apply resizing/dragging at the end of the frame
1473
0
    if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)
1474
0
    {
1475
0
        ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];
1476
0
        const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale));
1477
0
        const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);
1478
0
        table->ResizedColumnNextWidth = new_width;
1479
0
    }
1480
1481
0
    table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false);
1482
1483
    // Pop from id stack
1484
0
    IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!");
1485
0
    IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!");
1486
0
    if (table->InstanceCurrent > 0)
1487
0
        PopID();
1488
0
    PopID();
1489
1490
    // Restore window data that we modified
1491
0
    const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos;
1492
0
    inner_window->WorkRect = temp_data->HostBackupWorkRect;
1493
0
    inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect;
1494
0
    inner_window->SkipItems = table->HostSkipItems;
1495
0
    outer_window->DC.CursorPos = table->OuterRect.Min;
1496
0
    outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth;
1497
0
    outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize;
1498
0
    outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset;
1499
1500
    // Layout in outer window
1501
    // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding
1502
    // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)
1503
0
    if (inner_window != outer_window)
1504
0
    {
1505
0
        short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
1506
0
        inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.
1507
0
        g.CurrentTable = NULL; // To avoid error recovery recursing
1508
0
        EndChild();
1509
0
        g.CurrentTable = table;
1510
0
        inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
1511
0
    }
1512
0
    else
1513
0
    {
1514
0
        table->InnerWindow->DC.TreeDepth--;
1515
0
        ItemSize(table->OuterRect.GetSize());
1516
0
        ItemAdd(table->OuterRect, 0);
1517
0
    }
1518
1519
    // Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar
1520
0
    if (table->Flags & ImGuiTableFlags_NoHostExtendX)
1521
0
    {
1522
        // FIXME-TABLE: Could we remove this section?
1523
        // ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents
1524
0
        IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0);
1525
0
        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth);
1526
0
    }
1527
0
    else if (temp_data->UserOuterSize.x <= 0.0f)
1528
0
    {
1529
        // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block
1530
        // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.
1531
        // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback.
1532
0
        const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y
1533
0
        const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f);
1534
0
        outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x);
1535
0
        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size));
1536
0
    }
1537
0
    else
1538
0
    {
1539
0
        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x);
1540
0
    }
1541
0
    if (temp_data->UserOuterSize.y <= 0.0f)
1542
0
    {
1543
0
        const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f;
1544
0
        outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);
1545
0
        outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size));
1546
0
    }
1547
0
    else
1548
0
    {
1549
        // OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags_NoHostExtendY is set)
1550
0
        outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table->OuterRect.Max.y);
1551
0
    }
1552
1553
    // Save settings
1554
0
    if (table->IsSettingsDirty)
1555
0
        TableSaveSettings(table);
1556
0
    table->IsInitializing = false;
1557
1558
    // Clear or restore current table, if any
1559
0
    IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);
1560
0
    IM_ASSERT(g.TablesTempDataStacked > 0);
1561
0
    temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL;
1562
0
    g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;
1563
0
    if (g.CurrentTable)
1564
0
    {
1565
0
        g.CurrentTable->TempData = temp_data;
1566
0
        g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter;
1567
0
    }
1568
0
    outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1;
1569
0
    NavUpdateCurrentWindowIsScrollPushableX();
1570
0
}
1571
1572
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
1573
// 'init_mask' specify which fields to initialize.
1574
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
1575
0
{
1576
0
    ImGuiTableColumnFlags flags = column->Flags;
1577
0
    if (init_mask & ImGuiTableFlags_Resizable)
1578
0
    {
1579
0
        float init_width_or_weight = column->InitStretchWeightOrWidth;
1580
0
        column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
1581
0
        column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;
1582
0
        if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified
1583
0
            column->AutoFitQueue = 0x00;
1584
0
    }
1585
0
    if (init_mask & ImGuiTableFlags_Reorderable)
1586
0
        column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
1587
0
    if (init_mask & ImGuiTableFlags_Hideable)
1588
0
        column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
1589
0
    if (init_mask & ImGuiTableFlags_Sortable)
1590
0
    {
1591
        // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
1592
0
        column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;
1593
0
        column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;
1594
0
    }
1595
0
}
1596
1597
// See "COLUMNS SIZING POLICIES" comments at the top of this file
1598
// If (init_width_or_weight <= 0.0f) it is ignored
1599
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
1600
0
{
1601
0
    ImGuiContext& g = *GImGui;
1602
0
    ImGuiTable* table = g.CurrentTable;
1603
0
    if (table == NULL)
1604
0
    {
1605
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
1606
0
        return;
1607
0
    }
1608
0
    IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
1609
0
    IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
1610
0
    if (table->DeclColumnsCount >= table->ColumnsCount)
1611
0
    {
1612
0
        IM_ASSERT_USER_ERROR(table->DeclColumnsCount < table->ColumnsCount, "Called TableSetupColumn() too many times!");
1613
0
        return;
1614
0
    }
1615
1616
0
    ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];
1617
0
    table->DeclColumnsCount++;
1618
1619
    // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa.
1620
    // Give a grace to users of ImGuiTableFlags_ScrollX.
1621
0
    if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0)
1622
0
        IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column.");
1623
1624
    // When passing a width automatically enforce WidthFixed policy
1625
    // (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
1626
0
    if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
1627
0
        if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
1628
0
            flags |= ImGuiTableColumnFlags_WidthFixed;
1629
0
    if (flags & ImGuiTableColumnFlags_AngledHeader)
1630
0
    {
1631
0
        flags |= ImGuiTableColumnFlags_NoHeaderLabel;
1632
0
        table->AngledHeadersCount++;
1633
0
    }
1634
1635
0
    TableSetupColumnFlags(table, column, flags);
1636
0
    column->UserID = user_id;
1637
0
    flags = column->Flags;
1638
1639
    // Initialize defaults
1640
0
    column->InitStretchWeightOrWidth = init_width_or_weight;
1641
0
    if (table->IsInitializing)
1642
0
    {
1643
0
        ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
1644
0
        if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
1645
0
            init_flags |= ImGuiTableFlags_Resizable;
1646
0
        TableInitColumnDefaults(table, column, init_flags);
1647
0
    }
1648
1649
    // Store name (append with zero-terminator in contiguous buffer)
1650
    // FIXME: If we recorded the number of \n in names we could compute header row height
1651
0
    column->NameOffset = -1;
1652
0
    if (label != NULL && label[0] != 0)
1653
0
    {
1654
0
        column->NameOffset = (ImS16)table->ColumnsNames.size();
1655
0
        table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
1656
0
    }
1657
0
}
1658
1659
// [Public]
1660
void ImGui::TableSetupScrollFreeze(int columns, int rows)
1661
0
{
1662
0
    ImGuiContext& g = *GImGui;
1663
0
    ImGuiTable* table = g.CurrentTable;
1664
0
    if (table == NULL)
1665
0
    {
1666
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
1667
0
        return;
1668
0
    }
1669
0
    IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
1670
0
    IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
1671
0
    IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
1672
1673
0
    table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0;
1674
0
    table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0;
1675
0
    table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0;
1676
0
    table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0;
1677
0
    table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b
1678
1679
    // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered.
1680
    // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section)
1681
0
    for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++)
1682
0
    {
1683
0
        int order_n = table->DisplayOrderToIndex[column_n];
1684
0
        if (order_n != column_n && order_n >= table->FreezeColumnsRequest)
1685
0
        {
1686
0
            ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder);
1687
0
            ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]);
1688
0
        }
1689
0
    }
1690
0
}
1691
1692
//-----------------------------------------------------------------------------
1693
// [SECTION] Tables: Simple accessors
1694
//-----------------------------------------------------------------------------
1695
// - TableGetColumnCount()
1696
// - TableGetColumnName()
1697
// - TableGetColumnName() [Internal]
1698
// - TableSetColumnEnabled()
1699
// - TableGetColumnFlags()
1700
// - TableGetCellBgRect() [Internal]
1701
// - TableGetColumnResizeID() [Internal]
1702
// - TableGetHoveredColumn() [Internal]
1703
// - TableGetHoveredRow() [Internal]
1704
// - TableSetBgColor()
1705
//-----------------------------------------------------------------------------
1706
1707
int ImGui::TableGetColumnCount()
1708
0
{
1709
0
    ImGuiContext& g = *GImGui;
1710
0
    ImGuiTable* table = g.CurrentTable;
1711
0
    return table ? table->ColumnsCount : 0;
1712
0
}
1713
1714
const char* ImGui::TableGetColumnName(int column_n)
1715
0
{
1716
0
    ImGuiContext& g = *GImGui;
1717
0
    ImGuiTable* table = g.CurrentTable;
1718
0
    if (!table)
1719
0
        return NULL;
1720
0
    if (column_n < 0)
1721
0
        column_n = table->CurrentColumn;
1722
0
    return TableGetColumnName(table, column_n);
1723
0
}
1724
1725
const char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n)
1726
0
{
1727
0
    if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount)
1728
0
        return ""; // NameOffset is invalid at this point
1729
0
    const ImGuiTableColumn* column = &table->Columns[column_n];
1730
0
    if (column->NameOffset == -1)
1731
0
        return "";
1732
0
    return &table->ColumnsNames.Buf[column->NameOffset];
1733
0
}
1734
1735
// Change user accessible enabled/disabled state of a column (often perceived as "showing/hiding" from users point of view)
1736
// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)
1737
// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state.
1738
// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable().
1739
// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0.
1740
// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu.
1741
void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
1742
0
{
1743
0
    ImGuiContext& g = *GImGui;
1744
0
    ImGuiTable* table = g.CurrentTable;
1745
0
    if (table == NULL)
1746
0
    {
1747
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
1748
0
        return;
1749
0
    }
1750
0
    IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
1751
0
    if (column_n < 0)
1752
0
        column_n = table->CurrentColumn;
1753
0
    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
1754
0
    ImGuiTableColumn* column = &table->Columns[column_n];
1755
0
    column->IsUserEnabledNextFrame = enabled;
1756
0
}
1757
1758
// We allow querying for an extra column in order to poll the IsHovered state of the right-most section
1759
ImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n)
1760
0
{
1761
0
    ImGuiContext& g = *GImGui;
1762
0
    ImGuiTable* table = g.CurrentTable;
1763
0
    if (!table)
1764
0
        return ImGuiTableColumnFlags_None;
1765
0
    if (column_n < 0)
1766
0
        column_n = table->CurrentColumn;
1767
0
    if (column_n == table->ColumnsCount)
1768
0
        return (table->HoveredColumnBody == column_n) ? ImGuiTableColumnFlags_IsHovered : ImGuiTableColumnFlags_None;
1769
0
    return table->Columns[column_n].Flags;
1770
0
}
1771
1772
// Return the cell rectangle based on currently known height.
1773
// - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations.
1774
//   The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height.
1775
// - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right
1776
//   columns report a small offset so their CellBgRect can extend up to the outer border.
1777
//   FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling.
1778
ImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n)
1779
0
{
1780
0
    const ImGuiTableColumn* column = &table->Columns[column_n];
1781
0
    float x1 = column->MinX;
1782
0
    float x2 = column->MaxX;
1783
    //if (column->PrevEnabledColumn == -1)
1784
    //    x1 -= table->OuterPaddingX;
1785
    //if (column->NextEnabledColumn == -1)
1786
    //    x2 += table->OuterPaddingX;
1787
0
    x1 = ImMax(x1, table->WorkRect.Min.x);
1788
0
    x2 = ImMin(x2, table->WorkRect.Max.x);
1789
0
    return ImRect(x1, table->RowPosY1, x2, table->RowPosY2);
1790
0
}
1791
1792
// Return the resizing ID for the right-side of the given column.
1793
ImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no)
1794
0
{
1795
0
    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
1796
0
    ImGuiID instance_id = TableGetInstanceID(table, instance_no);
1797
0
    return instance_id + 1 + column_n; // FIXME: #6140: still not ideal
1798
0
}
1799
1800
// Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column.
1801
int ImGui::TableGetHoveredColumn()
1802
0
{
1803
0
    ImGuiContext& g = *GImGui;
1804
0
    ImGuiTable* table = g.CurrentTable;
1805
0
    if (!table)
1806
0
        return -1;
1807
0
    return (int)table->HoveredColumnBody;
1808
0
}
1809
1810
// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row.
1811
// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value.
1812
// This difference with is the reason why this is not public yet.
1813
int ImGui::TableGetHoveredRow()
1814
0
{
1815
0
    ImGuiContext& g = *GImGui;
1816
0
    ImGuiTable* table = g.CurrentTable;
1817
0
    if (!table)
1818
0
        return -1;
1819
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
1820
0
    return (int)table_instance->HoveredRowLast;
1821
0
}
1822
1823
void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n)
1824
0
{
1825
0
    ImGuiContext& g = *GImGui;
1826
0
    ImGuiTable* table = g.CurrentTable;
1827
0
    IM_ASSERT(target != ImGuiTableBgTarget_None);
1828
0
    if (table == NULL)
1829
0
    {
1830
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
1831
0
        return;
1832
0
    }
1833
1834
0
    if (color == IM_COL32_DISABLE)
1835
0
        color = 0;
1836
1837
    // We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time.
1838
0
    switch (target)
1839
0
    {
1840
0
    case ImGuiTableBgTarget_CellBg:
1841
0
    {
1842
0
        if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard
1843
0
            return;
1844
0
        if (column_n == -1)
1845
0
            column_n = table->CurrentColumn;
1846
0
        if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))
1847
0
            return;
1848
0
        if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)
1849
0
            table->RowCellDataCurrent++;
1850
0
        ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCurrent];
1851
0
        cell_data->BgColor = color;
1852
0
        cell_data->Column = (ImGuiTableColumnIdx)column_n;
1853
0
        break;
1854
0
    }
1855
0
    case ImGuiTableBgTarget_RowBg0:
1856
0
    case ImGuiTableBgTarget_RowBg1:
1857
0
    {
1858
0
        if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard
1859
0
            return;
1860
0
        IM_ASSERT(column_n == -1);
1861
0
        int bg_idx = (target == ImGuiTableBgTarget_RowBg1) ? 1 : 0;
1862
0
        table->RowBgColor[bg_idx] = color;
1863
0
        break;
1864
0
    }
1865
0
    default:
1866
0
        IM_ASSERT(0);
1867
0
    }
1868
0
}
1869
1870
//-------------------------------------------------------------------------
1871
// [SECTION] Tables: Row changes
1872
//-------------------------------------------------------------------------
1873
// - TableGetRowIndex()
1874
// - TableNextRow()
1875
// - TableBeginRow() [Internal]
1876
// - TableEndRow() [Internal]
1877
//-------------------------------------------------------------------------
1878
1879
// [Public] Note: for row coloring we use ->RowBgColorCounter which is the same value without counting header rows
1880
int ImGui::TableGetRowIndex()
1881
0
{
1882
0
    ImGuiContext& g = *GImGui;
1883
0
    ImGuiTable* table = g.CurrentTable;
1884
0
    if (!table)
1885
0
        return 0;
1886
0
    return table->CurrentRow;
1887
0
}
1888
1889
// [Public] Starts into the first cell of a new row
1890
void ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height)
1891
0
{
1892
0
    ImGuiContext& g = *GImGui;
1893
0
    ImGuiTable* table = g.CurrentTable;
1894
1895
0
    if (!table->IsLayoutLocked)
1896
0
        TableUpdateLayout(table);
1897
0
    if (table->IsInsideRow)
1898
0
        TableEndRow(table);
1899
1900
0
    table->LastRowFlags = table->RowFlags;
1901
0
    table->RowFlags = row_flags;
1902
0
    table->RowCellPaddingY = g.Style.CellPadding.y;
1903
0
    table->RowMinHeight = row_min_height;
1904
0
    TableBeginRow(table);
1905
1906
    // We honor min_row_height requested by user, but cannot guarantee per-row maximum height,
1907
    // because that would essentially require a unique clipping rectangle per-cell.
1908
0
    table->RowPosY2 += table->RowCellPaddingY * 2.0f;
1909
0
    table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height);
1910
1911
    // Disable output until user calls TableNextColumn()
1912
0
    table->InnerWindow->SkipItems = true;
1913
0
}
1914
1915
// [Internal] Only called by TableNextRow()
1916
void ImGui::TableBeginRow(ImGuiTable* table)
1917
0
{
1918
0
    ImGuiWindow* window = table->InnerWindow;
1919
0
    IM_ASSERT(!table->IsInsideRow);
1920
1921
    // New row
1922
0
    table->CurrentRow++;
1923
0
    table->CurrentColumn = -1;
1924
0
    table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE;
1925
0
    table->RowCellDataCurrent = -1;
1926
0
    table->IsInsideRow = true;
1927
1928
    // Begin frozen rows
1929
0
    float next_y1 = table->RowPosY2;
1930
0
    if (table->CurrentRow == 0 && table->FreezeRowsCount > 0)
1931
0
        next_y1 = window->DC.CursorPos.y = table->OuterRect.Min.y;
1932
1933
0
    table->RowPosY1 = table->RowPosY2 = next_y1;
1934
0
    table->RowTextBaseline = 0.0f;
1935
0
    table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent
1936
1937
0
    window->DC.PrevLineTextBaseOffset = 0.0f;
1938
0
    window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns.
1939
0
    window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.
1940
0
    window->DC.IsSameLine = window->DC.IsSetPos = false;
1941
0
    window->DC.CursorMaxPos.y = next_y1;
1942
1943
    // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.
1944
0
    if (table->RowFlags & ImGuiTableRowFlags_Headers)
1945
0
    {
1946
0
        TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg));
1947
0
        if (table->CurrentRow == 0)
1948
0
            table->IsUsingHeaders = true;
1949
0
    }
1950
0
}
1951
1952
// [Internal] Called by TableNextRow()
1953
void ImGui::TableEndRow(ImGuiTable* table)
1954
0
{
1955
0
    ImGuiContext& g = *GImGui;
1956
0
    ImGuiWindow* window = g.CurrentWindow;
1957
0
    IM_ASSERT(window == table->InnerWindow);
1958
0
    IM_ASSERT(table->IsInsideRow);
1959
1960
0
    if (table->CurrentColumn != -1)
1961
0
    {
1962
0
        TableEndCell(table);
1963
0
        table->CurrentColumn = -1;
1964
0
    }
1965
1966
    // Logging
1967
0
    if (g.LogEnabled)
1968
0
        LogRenderedText(NULL, "|");
1969
1970
    // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is
1971
    // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding.
1972
0
    window->DC.CursorPos.y = table->RowPosY2;
1973
1974
    // Row background fill
1975
0
    const float bg_y1 = table->RowPosY1;
1976
0
    const float bg_y2 = table->RowPosY2;
1977
0
    const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount);
1978
0
    const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest);
1979
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
1980
0
    if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers)))
1981
0
        table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1;
1982
1983
0
    const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y);
1984
0
    if (is_visible)
1985
0
    {
1986
        // Update data for TableGetHoveredRow()
1987
0
        if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0)
1988
0
            table_instance->HoveredRowNext = table->CurrentRow;
1989
1990
        // Decide of background color for the row
1991
0
        ImU32 bg_col0 = 0;
1992
0
        ImU32 bg_col1 = 0;
1993
0
        if (table->RowBgColor[0] != IM_COL32_DISABLE)
1994
0
            bg_col0 = table->RowBgColor[0];
1995
0
        else if (table->Flags & ImGuiTableFlags_RowBg)
1996
0
            bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg);
1997
0
        if (table->RowBgColor[1] != IM_COL32_DISABLE)
1998
0
            bg_col1 = table->RowBgColor[1];
1999
2000
        // Decide of top border color
2001
0
        ImU32 top_border_col = 0;
2002
0
        const float border_size = TABLE_BORDER_SIZE;
2003
0
        if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH))
2004
0
            top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight;
2005
2006
0
        const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0;
2007
0
        const bool draw_strong_bottom_border = unfreeze_rows_actual;
2008
0
        if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color)
2009
0
        {
2010
            // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is
2011
            // always followed by a change of clipping rectangle we perform the smallest overwrite possible here.
2012
0
            if ((table->Flags & ImGuiTableFlags_NoClip) == 0)
2013
0
                window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4();
2014
0
            table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0);
2015
0
        }
2016
2017
        // Draw row background
2018
        // We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle
2019
0
        if (bg_col0 || bg_col1)
2020
0
        {
2021
0
            ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2);
2022
0
            row_rect.ClipWith(table->BgClipRect);
2023
0
            if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y)
2024
0
                window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0);
2025
0
            if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y)
2026
0
                window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1);
2027
0
        }
2028
2029
        // Draw cell background color
2030
0
        if (draw_cell_bg_color)
2031
0
        {
2032
0
            ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent];
2033
0
            for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++)
2034
0
            {
2035
                // As we render the BG here we need to clip things (for layout we would not)
2036
                // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling.
2037
0
                const ImGuiTableColumn* column = &table->Columns[cell_data->Column];
2038
0
                ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column);
2039
0
                cell_bg_rect.ClipWith(table->BgClipRect);
2040
0
                cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x);     // So that first column after frozen one gets clipped when scrolling
2041
0
                cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);
2042
0
                if (cell_bg_rect.Min.y < cell_bg_rect.Max.y)
2043
0
                    window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
2044
0
            }
2045
0
        }
2046
2047
        // Draw top border
2048
0
        if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y)
2049
0
            window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size);
2050
2051
        // Draw bottom border at the row unfreezing mark (always strong)
2052
0
        if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y)
2053
0
            window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size);
2054
0
    }
2055
2056
    // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)
2057
    // - We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark
2058
    //   end of row and get the new cursor position.
2059
0
    if (unfreeze_rows_request)
2060
0
    {
2061
0
        IM_ASSERT(table->FreezeRowsRequest > 0);
2062
0
        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2063
0
            table->Columns[column_n].NavLayerCurrent = table->NavLayer;
2064
0
        const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
2065
0
        table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
2066
2067
0
        if (unfreeze_rows_actual)
2068
0
        {
2069
0
            IM_ASSERT(table->IsUnfrozenRows == false);
2070
0
            table->IsUnfrozenRows = true;
2071
2072
            // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
2073
0
            table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
2074
0
            table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
2075
0
            table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
2076
0
            IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
2077
2078
0
            float row_height = table->RowPosY2 - table->RowPosY1;
2079
0
            table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
2080
0
            table->RowPosY1 = table->RowPosY2 - row_height;
2081
0
            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2082
0
            {
2083
0
                ImGuiTableColumn* column = &table->Columns[column_n];
2084
0
                column->DrawChannelCurrent = column->DrawChannelUnfrozen;
2085
0
                column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
2086
0
            }
2087
2088
            // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
2089
0
            SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
2090
0
            table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
2091
0
        }
2092
0
    }
2093
2094
0
    if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
2095
0
        table->RowBgColorCounter++;
2096
0
    table->IsInsideRow = false;
2097
0
}
2098
2099
//-------------------------------------------------------------------------
2100
// [SECTION] Tables: Columns changes
2101
//-------------------------------------------------------------------------
2102
// - TableGetColumnIndex()
2103
// - TableSetColumnIndex()
2104
// - TableNextColumn()
2105
// - TableBeginCell() [Internal]
2106
// - TableEndCell() [Internal]
2107
//-------------------------------------------------------------------------
2108
2109
int ImGui::TableGetColumnIndex()
2110
0
{
2111
0
    ImGuiContext& g = *GImGui;
2112
0
    ImGuiTable* table = g.CurrentTable;
2113
0
    if (!table)
2114
0
        return 0;
2115
0
    return table->CurrentColumn;
2116
0
}
2117
2118
// [Public] Append into a specific column
2119
bool ImGui::TableSetColumnIndex(int column_n)
2120
0
{
2121
0
    ImGuiContext& g = *GImGui;
2122
0
    ImGuiTable* table = g.CurrentTable;
2123
0
    if (!table)
2124
0
        return false;
2125
2126
0
    if (table->CurrentColumn != column_n)
2127
0
    {
2128
0
        if (table->CurrentColumn != -1)
2129
0
            TableEndCell(table);
2130
0
        if ((column_n >= 0 && column_n < table->ColumnsCount) == false)
2131
0
        {
2132
0
            IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!");
2133
0
            return false;
2134
0
        }
2135
0
        TableBeginCell(table, column_n);
2136
0
    }
2137
2138
    // Return whether the column is visible. User may choose to skip submitting items based on this return value,
2139
    // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
2140
0
    return table->Columns[column_n].IsRequestOutput;
2141
0
}
2142
2143
// [Public] Append into the next column, wrap and create a new row when already on last column
2144
bool ImGui::TableNextColumn()
2145
0
{
2146
0
    ImGuiContext& g = *GImGui;
2147
0
    ImGuiTable* table = g.CurrentTable;
2148
0
    if (!table)
2149
0
        return false;
2150
2151
0
    if (table->IsInsideRow && table->CurrentColumn + 1 < table->ColumnsCount)
2152
0
    {
2153
0
        if (table->CurrentColumn != -1)
2154
0
            TableEndCell(table);
2155
0
        TableBeginCell(table, table->CurrentColumn + 1);
2156
0
    }
2157
0
    else
2158
0
    {
2159
0
        TableNextRow();
2160
0
        TableBeginCell(table, 0);
2161
0
    }
2162
2163
    // Return whether the column is visible. User may choose to skip submitting items based on this return value,
2164
    // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.
2165
0
    return table->Columns[table->CurrentColumn].IsRequestOutput;
2166
0
}
2167
2168
2169
// [Internal] Called by TableSetColumnIndex()/TableNextColumn()
2170
// This is called very frequently, so we need to be mindful of unnecessary overhead.
2171
// FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns.
2172
void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
2173
0
{
2174
0
    ImGuiContext& g = *GImGui;
2175
0
    ImGuiTableColumn* column = &table->Columns[column_n];
2176
0
    ImGuiWindow* window = table->InnerWindow;
2177
0
    table->CurrentColumn = column_n;
2178
2179
    // Start position is roughly ~~ CellRect.Min + CellPadding + Indent
2180
0
    float start_x = column->WorkMinX;
2181
0
    if (column->Flags & ImGuiTableColumnFlags_IndentEnable)
2182
0
        start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row.
2183
2184
0
    window->DC.CursorPos.x = start_x;
2185
0
    window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY;
2186
0
    window->DC.CursorMaxPos.x = window->DC.CursorPos.x;
2187
0
    window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT
2188
0
    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.
2189
0
    window->DC.CurrLineTextBaseOffset = table->RowTextBaseline;
2190
0
    window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent;
2191
2192
    // Note how WorkRect.Max.y is only set once during layout
2193
0
    window->WorkRect.Min.y = window->DC.CursorPos.y;
2194
0
    window->WorkRect.Min.x = column->WorkMinX;
2195
0
    window->WorkRect.Max.x = column->WorkMaxX;
2196
0
    window->DC.ItemWidth = column->ItemWidth;
2197
2198
0
    window->SkipItems = column->IsSkipItems;
2199
0
    if (column->IsSkipItems)
2200
0
    {
2201
0
        g.LastItemData.ID = 0;
2202
0
        g.LastItemData.StatusFlags = 0;
2203
0
    }
2204
2205
    // Also see TablePushColumnChannel()
2206
0
    if (table->Flags & ImGuiTableFlags_NoClip)
2207
0
    {
2208
        // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
2209
0
        table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
2210
        //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP);
2211
0
    }
2212
0
    else
2213
0
    {
2214
        // FIXME-TABLE: Could avoid this if draw channel is dummy channel?
2215
0
        SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
2216
0
        table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
2217
0
    }
2218
2219
    // Logging
2220
0
    if (g.LogEnabled && !column->IsSkipItems)
2221
0
    {
2222
0
        LogRenderedText(&window->DC.CursorPos, "|");
2223
0
        g.LogLinePosY = FLT_MAX;
2224
0
    }
2225
0
}
2226
2227
// [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn()
2228
void ImGui::TableEndCell(ImGuiTable* table)
2229
0
{
2230
0
    ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
2231
0
    ImGuiWindow* window = table->InnerWindow;
2232
2233
0
    if (window->DC.IsSetPos)
2234
0
        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();
2235
2236
    // Report maximum position so we can infer content size per column.
2237
0
    float* p_max_pos_x;
2238
0
    if (table->RowFlags & ImGuiTableRowFlags_Headers)
2239
0
        p_max_pos_x = &column->ContentMaxXHeadersUsed;  // Useful in case user submit contents in header row that is not a TableHeader() call
2240
0
    else
2241
0
        p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen;
2242
0
    *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x);
2243
0
    if (column->IsEnabled)
2244
0
        table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY);
2245
0
    column->ItemWidth = window->DC.ItemWidth;
2246
2247
    // Propagate text baseline for the entire row
2248
    // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.
2249
0
    table->RowTextBaseline = ImMax(table->RowTextBaseline, window->DC.PrevLineTextBaseOffset);
2250
0
}
2251
2252
//-------------------------------------------------------------------------
2253
// [SECTION] Tables: Columns width management
2254
//-------------------------------------------------------------------------
2255
// - TableGetMaxColumnWidth() [Internal]
2256
// - TableGetColumnWidthAuto() [Internal]
2257
// - TableSetColumnWidth()
2258
// - TableSetColumnWidthAutoSingle() [Internal]
2259
// - TableSetColumnWidthAutoAll() [Internal]
2260
// - TableUpdateColumnsWeightFromWidth() [Internal]
2261
//-------------------------------------------------------------------------
2262
// Note that actual columns widths are computed in TableUpdateLayout().
2263
//-------------------------------------------------------------------------
2264
2265
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
2266
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
2267
0
{
2268
0
    const ImGuiTableColumn* column = &table->Columns[column_n];
2269
0
    float max_width = FLT_MAX;
2270
0
    const float min_column_distance = table->MinColumnWidth + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2;
2271
0
    if (table->Flags & ImGuiTableFlags_ScrollX)
2272
0
    {
2273
        // Frozen columns can't reach beyond visible width else scrolling will naturally break.
2274
        // (we use DisplayOrder as within a set of multiple frozen column reordering is possible)
2275
0
        if (column->DisplayOrder < table->FreezeColumnsRequest)
2276
0
        {
2277
0
            max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX;
2278
0
            max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2;
2279
0
        }
2280
0
    }
2281
0
    else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0)
2282
0
    {
2283
        // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make
2284
        // sure they are all visible. Because of this we also know that all of the columns will always fit in
2285
        // table->WorkRect and therefore in table->InnerRect (because ScrollX is off)
2286
        // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match.
2287
        // See "table_width_distrib" and "table_width_keep_visible" tests
2288
0
        max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX;
2289
        //max_width -= table->CellSpacingX1;
2290
0
        max_width -= table->CellSpacingX2;
2291
0
        max_width -= table->CellPaddingX * 2.0f;
2292
0
        max_width -= table->OuterPaddingX;
2293
0
    }
2294
0
    return max_width;
2295
0
}
2296
2297
// Note this is meant to be stored in column->WidthAuto, please generally use the WidthAuto field
2298
float ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column)
2299
0
{
2300
0
    const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX;
2301
0
    const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX;
2302
0
    float width_auto = content_width_body;
2303
0
    if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth))
2304
0
        width_auto = ImMax(width_auto, content_width_headers);
2305
2306
    // Non-resizable fixed columns preserve their requested width
2307
0
    if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f)
2308
0
        if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize))
2309
0
            width_auto = column->InitStretchWeightOrWidth;
2310
2311
0
    return ImMax(width_auto, table->MinColumnWidth);
2312
0
}
2313
2314
// 'width' = inner column width, without padding
2315
void ImGui::TableSetColumnWidth(int column_n, float width)
2316
0
{
2317
0
    ImGuiContext& g = *GImGui;
2318
0
    ImGuiTable* table = g.CurrentTable;
2319
0
    IM_ASSERT(table != NULL && table->IsLayoutLocked == false);
2320
0
    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);
2321
0
    ImGuiTableColumn* column_0 = &table->Columns[column_n];
2322
0
    float column_0_width = width;
2323
2324
    // Apply constraints early
2325
    // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
2326
0
    IM_ASSERT(table->MinColumnWidth > 0.0f);
2327
0
    const float min_width = table->MinColumnWidth;
2328
0
    const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
2329
0
    column_0_width = ImClamp(column_0_width, min_width, max_width);
2330
0
    if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
2331
0
        return;
2332
2333
    //IMGUI_DEBUG_PRINT("TableSetColumnWidth(%d, %.1f->%.1f)\n", column_0_idx, column_0->WidthGiven, column_0_width);
2334
0
    ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;
2335
2336
    // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.
2337
    // - All fixed: easy.
2338
    // - All stretch: easy.
2339
    // - One or more fixed + one stretch: easy.
2340
    // - One or more fixed + more than one stretch: tricky.
2341
    // Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here.
2342
2343
    // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
2344
    // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.
2345
    // Scenarios:
2346
    // - F1 F2 F3  resize from F1| or F2|   --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset.
2347
    // - F1 F2 F3  resize from F3|          --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered.
2348
    // - F1 F2 W3  resize from F1| or F2|   --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.
2349
    // - F1 F2 W3  resize from W3|          --> ok: no-op (disabled by Resize Rule 1)
2350
    // - W1 W2 W3  resize from W1| or W2|   --> ok
2351
    // - W1 W2 W3  resize from W3|          --> ok: no-op (disabled by Resize Rule 1)
2352
    // - W1 F2 F3  resize from F3|          --> ok: no-op (disabled by Resize Rule 1)
2353
    // - W1 F2     resize from F2|          --> ok: no-op (disabled by Resize Rule 1)
2354
    // - W1 W2 F3  resize from W1| or W2|   --> ok
2355
    // - W1 F2 W3  resize from W1| or F2|   --> ok
2356
    // - F1 W2 F3  resize from W2|          --> ok
2357
    // - F1 W3 F2  resize from W3|          --> ok
2358
    // - W1 F2 F3  resize from W1|          --> ok: equivalent to resizing |F2. F3 will not move.
2359
    // - W1 F2 F3  resize from F2|          --> ok
2360
    // All resizes from a Wx columns are locking other columns.
2361
2362
    // Possible improvements:
2363
    // - W1 W2 W3  resize W1|               --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns.
2364
    // - W3 F1 F2  resize W3|               --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix.
2365
2366
    // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().
2367
2368
    // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize.
2369
    // This is the preferred resize path
2370
0
    if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed)
2371
0
        if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder)
2372
0
        {
2373
0
            column_0->WidthRequest = column_0_width;
2374
0
            table->IsSettingsDirty = true;
2375
0
            return;
2376
0
        }
2377
2378
    // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column)
2379
0
    if (column_1 == NULL)
2380
0
        column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL;
2381
0
    if (column_1 == NULL)
2382
0
        return;
2383
2384
    // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.
2385
    // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)
2386
0
    float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);
2387
0
    column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;
2388
0
    IM_ASSERT(column_0_width > 0.0f && column_1_width > 0.0f);
2389
0
    column_0->WidthRequest = column_0_width;
2390
0
    column_1->WidthRequest = column_1_width;
2391
0
    if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch)
2392
0
        TableUpdateColumnsWeightFromWidth(table);
2393
0
    table->IsSettingsDirty = true;
2394
0
}
2395
2396
// Disable clipping then auto-fit, will take 2 frames
2397
// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns)
2398
void ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n)
2399
0
{
2400
    // Single auto width uses auto-fit
2401
0
    ImGuiTableColumn* column = &table->Columns[column_n];
2402
0
    if (!column->IsEnabled)
2403
0
        return;
2404
0
    column->CannotSkipItemsQueue = (1 << 0);
2405
0
    table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n;
2406
0
}
2407
2408
void ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table)
2409
0
{
2410
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2411
0
    {
2412
0
        ImGuiTableColumn* column = &table->Columns[column_n];
2413
0
        if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column
2414
0
            continue;
2415
0
        column->CannotSkipItemsQueue = (1 << 0);
2416
0
        column->AutoFitQueue = (1 << 1);
2417
0
    }
2418
0
}
2419
2420
void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table)
2421
0
{
2422
0
    IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1);
2423
2424
    // Measure existing quantities
2425
0
    float visible_weight = 0.0f;
2426
0
    float visible_width = 0.0f;
2427
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2428
0
    {
2429
0
        ImGuiTableColumn* column = &table->Columns[column_n];
2430
0
        if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
2431
0
            continue;
2432
0
        IM_ASSERT(column->StretchWeight > 0.0f);
2433
0
        visible_weight += column->StretchWeight;
2434
0
        visible_width += column->WidthRequest;
2435
0
    }
2436
0
    IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f);
2437
2438
    // Apply new weights
2439
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2440
0
    {
2441
0
        ImGuiTableColumn* column = &table->Columns[column_n];
2442
0
        if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))
2443
0
            continue;
2444
0
        column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight;
2445
0
        IM_ASSERT(column->StretchWeight > 0.0f);
2446
0
    }
2447
0
}
2448
2449
//-------------------------------------------------------------------------
2450
// [SECTION] Tables: Drawing
2451
//-------------------------------------------------------------------------
2452
// - TablePushBackgroundChannel() [Internal]
2453
// - TablePopBackgroundChannel() [Internal]
2454
// - TableSetupDrawChannels() [Internal]
2455
// - TableMergeDrawChannels() [Internal]
2456
// - TableGetColumnBorderCol() [Internal]
2457
// - TableDrawBorders() [Internal]
2458
//-------------------------------------------------------------------------
2459
2460
// Bg2 is used by Selectable (and possibly other widgets) to render to the background.
2461
// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect.
2462
void ImGui::TablePushBackgroundChannel()
2463
0
{
2464
0
    ImGuiContext& g = *GImGui;
2465
0
    ImGuiWindow* window = g.CurrentWindow;
2466
0
    ImGuiTable* table = g.CurrentTable;
2467
2468
    // Optimization: avoid SetCurrentChannel() + PushClipRect()
2469
0
    table->HostBackupInnerClipRect = window->ClipRect;
2470
0
    SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd);
2471
0
    table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent);
2472
0
}
2473
2474
void ImGui::TablePopBackgroundChannel()
2475
0
{
2476
0
    ImGuiContext& g = *GImGui;
2477
0
    ImGuiWindow* window = g.CurrentWindow;
2478
0
    ImGuiTable* table = g.CurrentTable;
2479
2480
    // Optimization: avoid PopClipRect() + SetCurrentChannel()
2481
0
    SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
2482
0
    table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
2483
0
}
2484
2485
// Also see TableBeginCell()
2486
void ImGui::TablePushColumnChannel(int column_n)
2487
0
{
2488
0
    ImGuiContext& g = *GImGui;
2489
0
    ImGuiTable* table = g.CurrentTable;
2490
2491
    // Optimization: avoid SetCurrentChannel() + PushClipRect()
2492
0
    if (table->Flags & ImGuiTableFlags_NoClip)
2493
0
        return;
2494
0
    ImGuiWindow* window = g.CurrentWindow;
2495
0
    const ImGuiTableColumn* column = &table->Columns[column_n];
2496
0
    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
2497
0
    table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
2498
0
}
2499
2500
void ImGui::TablePopColumnChannel()
2501
0
{
2502
0
    ImGuiContext& g = *GImGui;
2503
0
    ImGuiTable* table = g.CurrentTable;
2504
2505
    // Optimization: avoid PopClipRect() + SetCurrentChannel()
2506
0
    if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
2507
0
        return;
2508
0
    ImGuiWindow* window = g.CurrentWindow;
2509
0
    const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
2510
0
    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
2511
0
    table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
2512
0
}
2513
2514
// Allocate draw channels. Called by TableUpdateLayout()
2515
// - We allocate them following storage order instead of display order so reordering columns won't needlessly
2516
//   increase overall dormant memory cost.
2517
// - We isolate headers draw commands in their own channels instead of just altering clip rects.
2518
//   This is in order to facilitate merging of draw commands.
2519
// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.
2520
// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other
2521
//   channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
2522
// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for
2523
//   horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).
2524
// Draw channel allocation (before merging):
2525
// - NoClip                       --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
2526
// - Clip                         --> 2+D+N channels
2527
// - FreezeRows                   --> 2+D+N*2 (unless scrolling value is zero)
2528
// - FreezeRows || FreezeColunns  --> 3+D+N*2 (unless scrolling value is zero)
2529
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
2530
void ImGui::TableSetupDrawChannels(ImGuiTable* table)
2531
0
{
2532
0
    const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;
2533
0
    const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;
2534
0
    const int channels_for_bg = 1 + 1 * freeze_row_multiplier;
2535
0
    const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || (memcmp(table->VisibleMaskByIndex, table->EnabledMaskByIndex, ImBitArrayGetStorageSizeInBytes(table->ColumnsCount)) != 0)) ? +1 : 0;
2536
0
    const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;
2537
0
    table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total);
2538
0
    table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);
2539
0
    table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN;
2540
0
    table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN);
2541
2542
0
    int draw_channel_current = 2;
2543
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2544
0
    {
2545
0
        ImGuiTableColumn* column = &table->Columns[column_n];
2546
0
        if (column->IsVisibleX && column->IsVisibleY)
2547
0
        {
2548
0
            column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current);
2549
0
            column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0));
2550
0
            if (!(table->Flags & ImGuiTableFlags_NoClip))
2551
0
                draw_channel_current++;
2552
0
        }
2553
0
        else
2554
0
        {
2555
0
            column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel;
2556
0
        }
2557
0
        column->DrawChannelCurrent = column->DrawChannelFrozen;
2558
0
    }
2559
2560
    // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default.
2561
    // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect.
2562
    // (This technically isn't part of setting up draw channels, but is reasonably related to be done here)
2563
0
    table->BgClipRect = table->InnerClipRect;
2564
0
    table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect;
2565
0
    table->Bg2ClipRectForDrawCmd = table->HostClipRect;
2566
0
    IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y);
2567
0
}
2568
2569
// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().
2570
// For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect,
2571
// actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels().
2572
//
2573
// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve
2574
// this we merge their clip rect and make them contiguous in the channel list, so they can be merged
2575
// by the call to DrawSplitter.Merge() following to the call to this function.
2576
// We reorder draw commands by arranging them into a maximum of 4 distinct groups:
2577
//
2578
//   1 group:               2 groups:              2 groups:              4 groups:
2579
//   [ 0. ] no freeze       [ 0. ] row freeze      [ 01 ] col freeze      [ 01 ] row+col freeze
2580
//   [ .. ]  or no scroll   [ 2. ]  and v-scroll   [ .. ]  and h-scroll   [ 23 ]  and v+h-scroll
2581
//
2582
// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).
2583
// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group
2584
// based on its position (within frozen rows/columns groups or not).
2585
// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.
2586
// This function assume that each column are pointing to a distinct draw channel,
2587
// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask.
2588
//
2589
// Column channels will not be merged into one of the 1-4 groups in the following cases:
2590
// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).
2591
//   Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds
2592
//   matches, by e.g. calling SetCursorScreenPos().
2593
// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..
2594
//   we could do better but it's going to be rare and probably not worth the hassle.
2595
// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.
2596
//
2597
// This function is particularly tricky to understand.. take a breath.
2598
void ImGui::TableMergeDrawChannels(ImGuiTable* table)
2599
0
{
2600
0
    ImGuiContext& g = *GImGui;
2601
0
    ImDrawListSplitter* splitter = table->DrawSplitter;
2602
0
    const bool has_freeze_v = (table->FreezeRowsCount > 0);
2603
0
    const bool has_freeze_h = (table->FreezeColumnsCount > 0);
2604
0
    IM_ASSERT(splitter->_Current == 0);
2605
2606
    // Track which groups we are going to attempt to merge, and which channels goes into each group.
2607
0
    struct MergeGroup
2608
0
    {
2609
0
        ImRect          ClipRect;
2610
0
        int             ChannelsCount = 0;
2611
0
        ImBitArrayPtr   ChannelsMask = NULL;
2612
0
    };
2613
0
    int merge_group_mask = 0x00;
2614
0
    MergeGroup merge_groups[4];
2615
2616
    // Use a reusable temp buffer for the merge masks as they are dynamically sized.
2617
0
    const int max_draw_channels = (4 + table->ColumnsCount * 2);
2618
0
    const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels);
2619
0
    g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5);
2620
0
    memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5);
2621
0
    for (int n = 0; n < IM_ARRAYSIZE(merge_groups); n++)
2622
0
        merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n));
2623
0
    ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4));
2624
2625
    // 1. Scan channels and take note of those which can be merged
2626
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2627
0
    {
2628
0
        if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))
2629
0
            continue;
2630
0
        ImGuiTableColumn* column = &table->Columns[column_n];
2631
2632
0
        const int merge_group_sub_count = has_freeze_v ? 2 : 1;
2633
0
        for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++)
2634
0
        {
2635
0
            const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen;
2636
2637
            // Don't attempt to merge if there are multiple draw calls within the column
2638
0
            ImDrawChannel* src_channel = &splitter->_Channels[channel_no];
2639
0
            if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()
2640
0
                src_channel->_CmdBuffer.pop_back();
2641
0
            if (src_channel->_CmdBuffer.Size != 1)
2642
0
                continue;
2643
2644
            // Find out the width of this merge group and check if it will fit in our column
2645
            // (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it)
2646
0
            if (!(column->Flags & ImGuiTableColumnFlags_NoClip))
2647
0
            {
2648
0
                float content_max_x;
2649
0
                if (!has_freeze_v)
2650
0
                    content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze
2651
0
                else if (merge_group_sub_n == 0)
2652
0
                    content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed);   // Row freeze: use width before freeze
2653
0
                else
2654
0
                    content_max_x = column->ContentMaxXUnfrozen;                                        // Row freeze: use width after freeze
2655
0
                if (content_max_x > column->ClipRect.Max.x)
2656
0
                    continue;
2657
0
            }
2658
2659
0
            const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);
2660
0
            IM_ASSERT(channel_no < max_draw_channels);
2661
0
            MergeGroup* merge_group = &merge_groups[merge_group_n];
2662
0
            if (merge_group->ChannelsCount == 0)
2663
0
                merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
2664
0
            ImBitArraySetBit(merge_group->ChannelsMask, channel_no);
2665
0
            merge_group->ChannelsCount++;
2666
0
            merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);
2667
0
            merge_group_mask |= (1 << merge_group_n);
2668
0
        }
2669
2670
        // Invalidate current draw channel
2671
        // (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data)
2672
0
        column->DrawChannelCurrent = (ImGuiTableDrawChannelIdx)-1;
2673
0
    }
2674
2675
    // [DEBUG] Display merge groups
2676
#if 0
2677
    if (g.IO.KeyShift)
2678
        for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)
2679
        {
2680
            MergeGroup* merge_group = &merge_groups[merge_group_n];
2681
            if (merge_group->ChannelsCount == 0)
2682
                continue;
2683
            char buf[32];
2684
            ImFormatString(buf, 32, "MG%d:%d", merge_group_n, merge_group->ChannelsCount);
2685
            ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4);
2686
            ImVec2 text_size = CalcTextSize(buf, NULL);
2687
            GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255));
2688
            GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL);
2689
            GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255));
2690
        }
2691
#endif
2692
2693
    // 2. Rewrite channel list in our preferred order
2694
0
    if (merge_group_mask != 0)
2695
0
    {
2696
        // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels().
2697
0
        const int LEADING_DRAW_CHANNELS = 2;
2698
0
        g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized
2699
0
        ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;
2700
0
        ImBitArraySetBitRange(remaining_mask, LEADING_DRAW_CHANNELS, splitter->_Count);
2701
0
        ImBitArrayClearBit(remaining_mask, table->Bg2DrawChannelUnfrozen);
2702
0
        IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);
2703
0
        int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);
2704
        //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect;
2705
0
        ImRect host_rect = table->HostClipRect;
2706
0
        for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)
2707
0
        {
2708
0
            if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount)
2709
0
            {
2710
0
                MergeGroup* merge_group = &merge_groups[merge_group_n];
2711
0
                ImRect merge_clip_rect = merge_group->ClipRect;
2712
2713
                // Extend outer-most clip limits to match those of host, so draw calls can be merged even if
2714
                // outer-most columns have some outer padding offsetting them from their parent ClipRect.
2715
                // The principal cases this is dealing with are:
2716
                // - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge
2717
                // - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge
2718
                // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit
2719
                // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect.
2720
0
                if ((merge_group_n & 1) == 0 || !has_freeze_h)
2721
0
                    merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x);
2722
0
                if ((merge_group_n & 2) == 0 || !has_freeze_v)
2723
0
                    merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y);
2724
0
                if ((merge_group_n & 1) != 0)
2725
0
                    merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x);
2726
0
                if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0)
2727
0
                    merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y);
2728
                //GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); // [DEBUG]
2729
                //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200));
2730
                //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));
2731
0
                remaining_count -= merge_group->ChannelsCount;
2732
0
                for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++)
2733
0
                    remaining_mask[n] &= ~merge_group->ChannelsMask[n];
2734
0
                for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)
2735
0
                {
2736
                    // Copy + overwrite new clip rect
2737
0
                    if (!IM_BITARRAY_TESTBIT(merge_group->ChannelsMask, n))
2738
0
                        continue;
2739
0
                    IM_BITARRAY_CLEARBIT(merge_group->ChannelsMask, n);
2740
0
                    merge_channels_count--;
2741
2742
0
                    ImDrawChannel* channel = &splitter->_Channels[n];
2743
0
                    IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect)));
2744
0
                    channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4();
2745
0
                    memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
2746
0
                }
2747
0
            }
2748
2749
            // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1)
2750
0
            if (merge_group_n == 1 && has_freeze_v)
2751
0
                memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel));
2752
0
        }
2753
2754
        // Append unmergeable channels that we didn't reorder at the end of the list
2755
0
        for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)
2756
0
        {
2757
0
            if (!IM_BITARRAY_TESTBIT(remaining_mask, n))
2758
0
                continue;
2759
0
            ImDrawChannel* channel = &splitter->_Channels[n];
2760
0
            memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));
2761
0
            remaining_count--;
2762
0
        }
2763
0
        IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size);
2764
0
        memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel));
2765
0
    }
2766
0
}
2767
2768
static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n)
2769
0
{
2770
0
    const bool is_hovered = (table->HoveredColumnBorder == column_n);
2771
0
    const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);
2772
0
    const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);
2773
0
    if (is_resized || is_hovered)
2774
0
        return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);
2775
0
    if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)))
2776
0
        return table->BorderColorStrong;
2777
0
    return table->BorderColorLight;
2778
0
}
2779
2780
// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)
2781
void ImGui::TableDrawBorders(ImGuiTable* table)
2782
0
{
2783
0
    ImGuiWindow* inner_window = table->InnerWindow;
2784
0
    if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect))
2785
0
        return;
2786
2787
0
    ImDrawList* inner_drawlist = inner_window->DrawList;
2788
0
    table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0);
2789
0
    inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false);
2790
2791
    // Draw inner border and resizing feedback
2792
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
2793
0
    const float border_size = TABLE_BORDER_SIZE;
2794
0
    const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f);
2795
0
    const float draw_y2_body = table->InnerRect.Max.y;
2796
0
    const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1;
2797
0
    if (table->Flags & ImGuiTableFlags_BordersInnerV)
2798
0
    {
2799
0
        for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
2800
0
        {
2801
0
            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
2802
0
                continue;
2803
2804
0
            const int column_n = table->DisplayOrderToIndex[order_n];
2805
0
            ImGuiTableColumn* column = &table->Columns[column_n];
2806
0
            const bool is_hovered = (table->HoveredColumnBorder == column_n);
2807
0
            const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);
2808
0
            const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0;
2809
0
            const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);
2810
0
            if (column->MaxX > table->InnerClipRect.Max.x && !is_resized)
2811
0
                continue;
2812
2813
            // Decide whether right-most column is visible
2814
0
            if (column->NextEnabledColumn == -1 && !is_resizable)
2815
0
                if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX))
2816
0
                    continue;
2817
0
            if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..
2818
0
                continue;
2819
2820
            // Draw in outer window so right-most column won't be clipped
2821
0
            float draw_y2 = draw_y2_head;
2822
0
            if (is_frozen_separator)
2823
0
                draw_y2 = draw_y2_body;
2824
0
            else if ((table->Flags & ImGuiTableFlags_NoBordersInBodyUntilResize) != 0 && (is_hovered || is_resized))
2825
0
                draw_y2 = draw_y2_body;
2826
0
            else if ((table->Flags & (ImGuiTableFlags_NoBordersInBodyUntilResize | ImGuiTableFlags_NoBordersInBody)) == 0)
2827
0
                draw_y2 = draw_y2_body;
2828
0
            if (draw_y2 > draw_y1)
2829
0
                inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);
2830
0
        }
2831
0
    }
2832
2833
    // Draw outer border
2834
    // FIXME: could use AddRect or explicit VLine/HLine helper?
2835
0
    if (table->Flags & ImGuiTableFlags_BordersOuter)
2836
0
    {
2837
        // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call
2838
        // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their
2839
        // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part
2840
        // of it in inner window, and the part that's over scrollbars in the outer window..)
2841
        // Either solution currently won't allow us to use a larger border size: the border would clipped.
2842
0
        const ImRect outer_border = table->OuterRect;
2843
0
        const ImU32 outer_col = table->BorderColorStrong;
2844
0
        if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
2845
0
        {
2846
0
            inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
2847
0
        }
2848
0
        else if (table->Flags & ImGuiTableFlags_BordersOuterV)
2849
0
        {
2850
0
            inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size);
2851
0
            inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size);
2852
0
        }
2853
0
        else if (table->Flags & ImGuiTableFlags_BordersOuterH)
2854
0
        {
2855
0
            inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size);
2856
0
            inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size);
2857
0
        }
2858
0
    }
2859
0
    if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y)
2860
0
    {
2861
        // Draw bottom-most row border between it is above outer border.
2862
0
        const float border_y = table->RowPosY2;
2863
0
        if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y)
2864
0
            inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);
2865
0
    }
2866
2867
0
    inner_drawlist->PopClipRect();
2868
0
}
2869
2870
//-------------------------------------------------------------------------
2871
// [SECTION] Tables: Sorting
2872
//-------------------------------------------------------------------------
2873
// - TableGetSortSpecs()
2874
// - TableFixColumnSortDirection() [Internal]
2875
// - TableGetColumnNextSortDirection() [Internal]
2876
// - TableSetColumnSortDirection() [Internal]
2877
// - TableSortSpecsSanitize() [Internal]
2878
// - TableSortSpecsBuild() [Internal]
2879
//-------------------------------------------------------------------------
2880
2881
// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set)
2882
// When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have
2883
// changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,
2884
// else you may wastefully sort your data every frame!
2885
// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()!
2886
ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
2887
0
{
2888
0
    ImGuiContext& g = *GImGui;
2889
0
    ImGuiTable* table = g.CurrentTable;
2890
0
    if (table == NULL || !(table->Flags & ImGuiTableFlags_Sortable))
2891
0
        return NULL;
2892
2893
    // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.
2894
0
    if (!table->IsLayoutLocked)
2895
0
        TableUpdateLayout(table);
2896
2897
0
    TableSortSpecsBuild(table);
2898
0
    return &table->SortSpecs;
2899
0
}
2900
2901
static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)
2902
0
{
2903
0
    IM_ASSERT(n < column->SortDirectionsAvailCount);
2904
0
    return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03);
2905
0
}
2906
2907
// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)
2908
void ImGui::TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column)
2909
0
{
2910
0
    if (column->SortOrder == -1 || (column->SortDirectionsAvailMask & (1 << column->SortDirection)) != 0)
2911
0
        return;
2912
0
    column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0);
2913
0
    table->IsSortSpecsDirty = true;
2914
0
}
2915
2916
// Calculate next sort direction that would be set after clicking the column
2917
// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.
2918
// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.
2919
IM_STATIC_ASSERT(ImGuiSortDirection_None == 0 && ImGuiSortDirection_Ascending == 1 && ImGuiSortDirection_Descending == 2);
2920
ImGuiSortDirection ImGui::TableGetColumnNextSortDirection(ImGuiTableColumn* column)
2921
0
{
2922
0
    IM_ASSERT(column->SortDirectionsAvailCount > 0);
2923
0
    if (column->SortOrder == -1)
2924
0
        return TableGetColumnAvailSortDirection(column, 0);
2925
0
    for (int n = 0; n < 3; n++)
2926
0
        if (column->SortDirection == TableGetColumnAvailSortDirection(column, n))
2927
0
            return TableGetColumnAvailSortDirection(column, (n + 1) % column->SortDirectionsAvailCount);
2928
0
    IM_ASSERT(0);
2929
0
    return ImGuiSortDirection_None;
2930
0
}
2931
2932
// Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert
2933
// the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code.
2934
void ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs)
2935
0
{
2936
0
    ImGuiContext& g = *GImGui;
2937
0
    ImGuiTable* table = g.CurrentTable;
2938
2939
0
    if (!(table->Flags & ImGuiTableFlags_SortMulti))
2940
0
        append_to_sort_specs = false;
2941
0
    if (!(table->Flags & ImGuiTableFlags_SortTristate))
2942
0
        IM_ASSERT(sort_direction != ImGuiSortDirection_None);
2943
2944
0
    ImGuiTableColumnIdx sort_order_max = 0;
2945
0
    if (append_to_sort_specs)
2946
0
        for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
2947
0
            sort_order_max = ImMax(sort_order_max, table->Columns[other_column_n].SortOrder);
2948
2949
0
    ImGuiTableColumn* column = &table->Columns[column_n];
2950
0
    column->SortDirection = (ImU8)sort_direction;
2951
0
    if (column->SortDirection == ImGuiSortDirection_None)
2952
0
        column->SortOrder = -1;
2953
0
    else if (column->SortOrder == -1 || !append_to_sort_specs)
2954
0
        column->SortOrder = append_to_sort_specs ? sort_order_max + 1 : 0;
2955
2956
0
    for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
2957
0
    {
2958
0
        ImGuiTableColumn* other_column = &table->Columns[other_column_n];
2959
0
        if (other_column != column && !append_to_sort_specs)
2960
0
            other_column->SortOrder = -1;
2961
0
        TableFixColumnSortDirection(table, other_column);
2962
0
    }
2963
0
    table->IsSettingsDirty = true;
2964
0
    table->IsSortSpecsDirty = true;
2965
0
}
2966
2967
void ImGui::TableSortSpecsSanitize(ImGuiTable* table)
2968
0
{
2969
0
    IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);
2970
2971
    // Clear SortOrder from hidden column and verify that there's no gap or duplicate.
2972
0
    int sort_order_count = 0;
2973
0
    ImU64 sort_order_mask = 0x00;
2974
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2975
0
    {
2976
0
        ImGuiTableColumn* column = &table->Columns[column_n];
2977
0
        if (column->SortOrder != -1 && !column->IsEnabled)
2978
0
            column->SortOrder = -1;
2979
0
        if (column->SortOrder == -1)
2980
0
            continue;
2981
0
        sort_order_count++;
2982
0
        sort_order_mask |= ((ImU64)1 << column->SortOrder);
2983
0
        IM_ASSERT(sort_order_count < (int)sizeof(sort_order_mask) * 8);
2984
0
    }
2985
2986
0
    const bool need_fix_linearize = ((ImU64)1 << sort_order_count) != (sort_order_mask + 1);
2987
0
    const bool need_fix_single_sort_order = (sort_order_count > 1) && !(table->Flags & ImGuiTableFlags_SortMulti);
2988
0
    if (need_fix_linearize || need_fix_single_sort_order)
2989
0
    {
2990
0
        ImU64 fixed_mask = 0x00;
2991
0
        for (int sort_n = 0; sort_n < sort_order_count; sort_n++)
2992
0
        {
2993
            // Fix: Rewrite sort order fields if needed so they have no gap or duplicate.
2994
            // (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1)
2995
0
            int column_with_smallest_sort_order = -1;
2996
0
            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
2997
0
                if ((fixed_mask & ((ImU64)1 << (ImU64)column_n)) == 0 && table->Columns[column_n].SortOrder != -1)
2998
0
                    if (column_with_smallest_sort_order == -1 || table->Columns[column_n].SortOrder < table->Columns[column_with_smallest_sort_order].SortOrder)
2999
0
                        column_with_smallest_sort_order = column_n;
3000
0
            IM_ASSERT(column_with_smallest_sort_order != -1);
3001
0
            fixed_mask |= ((ImU64)1 << column_with_smallest_sort_order);
3002
0
            table->Columns[column_with_smallest_sort_order].SortOrder = (ImGuiTableColumnIdx)sort_n;
3003
3004
            // Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set.
3005
0
            if (need_fix_single_sort_order)
3006
0
            {
3007
0
                sort_order_count = 1;
3008
0
                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3009
0
                    if (column_n != column_with_smallest_sort_order)
3010
0
                        table->Columns[column_n].SortOrder = -1;
3011
0
                break;
3012
0
            }
3013
0
        }
3014
0
    }
3015
3016
    // Fallback default sort order (if no column with the ImGuiTableColumnFlags_DefaultSort flag)
3017
0
    if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))
3018
0
        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3019
0
        {
3020
0
            ImGuiTableColumn* column = &table->Columns[column_n];
3021
0
            if (column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_NoSort))
3022
0
            {
3023
0
                sort_order_count = 1;
3024
0
                column->SortOrder = 0;
3025
0
                column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0);
3026
0
                break;
3027
0
            }
3028
0
        }
3029
3030
0
    table->SortSpecsCount = (ImGuiTableColumnIdx)sort_order_count;
3031
0
}
3032
3033
void ImGui::TableSortSpecsBuild(ImGuiTable* table)
3034
0
{
3035
0
    bool dirty = table->IsSortSpecsDirty;
3036
0
    if (dirty)
3037
0
    {
3038
0
        TableSortSpecsSanitize(table);
3039
0
        table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount);
3040
0
        table->SortSpecs.SpecsDirty = true; // Mark as dirty for user
3041
0
        table->IsSortSpecsDirty = false; // Mark as not dirty for us
3042
0
    }
3043
3044
    // Write output
3045
    // May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()
3046
0
    ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
3047
0
    if (dirty && sort_specs != NULL)
3048
0
        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3049
0
        {
3050
0
            ImGuiTableColumn* column = &table->Columns[column_n];
3051
0
            if (column->SortOrder == -1)
3052
0
                continue;
3053
0
            IM_ASSERT(column->SortOrder < table->SortSpecsCount);
3054
0
            ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];
3055
0
            sort_spec->ColumnUserID = column->UserID;
3056
0
            sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
3057
0
            sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
3058
0
            sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
3059
0
        }
3060
3061
0
    table->SortSpecs.Specs = sort_specs;
3062
0
    table->SortSpecs.SpecsCount = table->SortSpecsCount;
3063
0
}
3064
3065
//-------------------------------------------------------------------------
3066
// [SECTION] Tables: Headers
3067
//-------------------------------------------------------------------------
3068
// - TableGetHeaderRowHeight() [Internal]
3069
// - TableGetHeaderAngledMaxLabelWidth() [Internal]
3070
// - TableHeadersRow()
3071
// - TableHeader()
3072
// - TableAngledHeadersRow()
3073
// - TableAngledHeadersRowEx() [Internal]
3074
//-------------------------------------------------------------------------
3075
3076
float ImGui::TableGetHeaderRowHeight()
3077
0
{
3078
    // Caring for a minor edge case:
3079
    // Calculate row height, for the unlikely case that some labels may be taller than others.
3080
    // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.
3081
    // In your custom header row you may omit this all together and just call TableNextRow() without a height...
3082
0
    ImGuiContext& g = *GImGui;
3083
0
    ImGuiTable* table = g.CurrentTable;
3084
0
    float row_height = g.FontSize;
3085
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3086
0
        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
3087
0
            if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0)
3088
0
                row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y);
3089
0
    return row_height + g.Style.CellPadding.y * 2.0f;
3090
0
}
3091
3092
float ImGui::TableGetHeaderAngledMaxLabelWidth()
3093
0
{
3094
0
    ImGuiContext& g = *GImGui;
3095
0
    ImGuiTable* table = g.CurrentTable;
3096
0
    float width = 0.0f;
3097
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3098
0
        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))
3099
0
            if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader)
3100
0
                width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x);
3101
0
    return width + g.Style.CellPadding.y * 2.0f; // Swap padding
3102
0
}
3103
3104
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
3105
// The intent is that advanced users willing to create customized headers would not need to use this helper
3106
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
3107
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
3108
// This code is intentionally written to not make much use of internal functions, to give you better direction
3109
// if you need to write your own.
3110
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
3111
void ImGui::TableHeadersRow()
3112
0
{
3113
0
    ImGuiContext& g = *GImGui;
3114
0
    ImGuiTable* table = g.CurrentTable;
3115
0
    if (table == NULL)
3116
0
    {
3117
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
3118
0
        return;
3119
0
    }
3120
3121
    // Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
3122
    // it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
3123
0
    if (!table->IsLayoutLocked)
3124
0
        TableUpdateLayout(table);
3125
3126
    // Open row
3127
0
    const float row_height = TableGetHeaderRowHeight();
3128
0
    TableNextRow(ImGuiTableRowFlags_Headers, row_height);
3129
0
    const float row_y1 = GetCursorScreenPos().y;
3130
0
    if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.
3131
0
        return;
3132
3133
0
    const int columns_count = TableGetColumnCount();
3134
0
    for (int column_n = 0; column_n < columns_count; column_n++)
3135
0
    {
3136
0
        if (!TableSetColumnIndex(column_n))
3137
0
            continue;
3138
3139
        // Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
3140
0
        const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
3141
0
        PushID(column_n);
3142
0
        TableHeader(name);
3143
0
        PopID();
3144
0
    }
3145
3146
    // Allow opening popup from the right-most section after the last column.
3147
0
    ImVec2 mouse_pos = ImGui::GetMousePos();
3148
0
    if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)
3149
0
        if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)
3150
0
            TableOpenContextMenu(columns_count); // Will open a non-column-specific popup.
3151
0
}
3152
3153
// Emit a column header (text + optional sort order)
3154
// We cpu-clip text here so that all columns headers can be merged into a same draw call.
3155
// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()
3156
void ImGui::TableHeader(const char* label)
3157
0
{
3158
0
    ImGuiContext& g = *GImGui;
3159
0
    ImGuiWindow* window = g.CurrentWindow;
3160
0
    if (window->SkipItems)
3161
0
        return;
3162
3163
0
    ImGuiTable* table = g.CurrentTable;
3164
0
    if (table == NULL)
3165
0
    {
3166
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
3167
0
        return;
3168
0
    }
3169
3170
0
    IM_ASSERT(table->CurrentColumn != -1);
3171
0
    const int column_n = table->CurrentColumn;
3172
0
    ImGuiTableColumn* column = &table->Columns[column_n];
3173
3174
    // Label
3175
0
    if (label == NULL)
3176
0
        label = "";
3177
0
    const char* label_end = FindRenderedTextEnd(label);
3178
0
    ImVec2 label_size = CalcTextSize(label, label_end, true);
3179
0
    ImVec2 label_pos = window->DC.CursorPos;
3180
3181
    // If we already got a row height, there's use that.
3182
    // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?
3183
0
    ImRect cell_r = TableGetCellBgRect(table, column_n);
3184
0
    float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f);
3185
3186
    // Calculate ideal size for sort order arrow
3187
0
    float w_arrow = 0.0f;
3188
0
    float w_sort_text = 0.0f;
3189
0
    bool sort_arrow = false;
3190
0
    char sort_order_suf[4] = "";
3191
0
    const float ARROW_SCALE = 0.65f;
3192
0
    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
3193
0
    {
3194
0
        w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);
3195
0
        if (column->SortOrder != -1)
3196
0
            sort_arrow = true;
3197
0
        if (column->SortOrder > 0)
3198
0
        {
3199
0
            ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1);
3200
0
            w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;
3201
0
        }
3202
0
    }
3203
3204
    // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.
3205
0
    float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;
3206
0
    column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x));
3207
0
    column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);
3208
3209
    // Keep header highlighted when context menu is open.
3210
0
    ImGuiID id = window->GetID(label);
3211
0
    ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));
3212
0
    ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal
3213
0
    if (!ItemAdd(bb, id))
3214
0
        return;
3215
3216
    //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
3217
    //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]
3218
3219
    // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.
3220
0
    const bool highlight = (table->HighlightColumnHeader == column_n);
3221
0
    bool hovered, held;
3222
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap);
3223
0
    if (held || hovered || highlight)
3224
0
    {
3225
0
        const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
3226
        //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
3227
0
        TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);
3228
0
    }
3229
0
    else
3230
0
    {
3231
        // Submit single cell bg color in the case we didn't submit a full header row
3232
0
        if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
3233
0
            TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
3234
0
    }
3235
0
    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
3236
0
    if (held)
3237
0
        table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
3238
0
    window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
3239
3240
    // Drag and drop to re-order columns.
3241
    // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.
3242
0
    if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)
3243
0
    {
3244
        // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x
3245
0
        table->ReorderColumn = (ImGuiTableColumnIdx)column_n;
3246
0
        table->InstanceInteracted = table->InstanceCurrent;
3247
3248
        // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.
3249
0
        if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)
3250
0
            if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)
3251
0
                if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))
3252
0
                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
3253
0
                        table->ReorderColumnDir = -1;
3254
0
        if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)
3255
0
            if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)
3256
0
                if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))
3257
0
                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))
3258
0
                        table->ReorderColumnDir = +1;
3259
0
    }
3260
3261
    // Sort order arrow
3262
0
    const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x);
3263
0
    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))
3264
0
    {
3265
0
        if (column->SortOrder != -1)
3266
0
        {
3267
0
            float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);
3268
0
            float y = label_pos.y;
3269
0
            if (column->SortOrder > 0)
3270
0
            {
3271
0
                PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));
3272
0
                RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);
3273
0
                PopStyleColor();
3274
0
                x += w_sort_text;
3275
0
            }
3276
0
            RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);
3277
0
        }
3278
3279
        // Handle clicking on column header to adjust Sort Order
3280
0
        if (pressed && table->ReorderColumn != column_n)
3281
0
        {
3282
0
            ImGuiSortDirection sort_direction = TableGetColumnNextSortDirection(column);
3283
0
            TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);
3284
0
        }
3285
0
    }
3286
3287
    // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
3288
    // be merged into a single draw call.
3289
    //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
3290
0
    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
3291
3292
0
    const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
3293
0
    if (text_clipped && hovered && g.ActiveId == 0)
3294
0
        SetItemTooltip("%.*s", (int)(label_end - label), label);
3295
3296
    // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden
3297
0
    if (IsMouseReleased(1) && IsItemHovered())
3298
0
        TableOpenContextMenu(column_n);
3299
0
}
3300
3301
// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
3302
// FIXME: No hit-testing/button on the angled header.
3303
void ImGui::TableAngledHeadersRow()
3304
0
{
3305
0
    ImGuiContext& g = *GImGui;
3306
0
    ImGuiTable* table = g.CurrentTable;
3307
0
    ImGuiTableTempData* temp_data = table->TempData;
3308
0
    temp_data->AngledHeadersRequests.resize(0);
3309
0
    temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount);
3310
3311
    // Which column needs highlight?
3312
0
    const ImGuiID row_id = GetID("##AngledHeaders");
3313
0
    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
3314
0
    int highlight_column_n = table->HighlightColumnHeader;
3315
0
    if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
3316
0
        if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
3317
0
            highlight_column_n = table->HoveredColumnBody;
3318
3319
    // Build up request
3320
0
    ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg);
3321
0
    ImU32 col_text = GetColorU32(ImGuiCol_Text);
3322
0
    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
3323
0
        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
3324
0
        {
3325
0
            const int column_n = table->DisplayOrderToIndex[order_n];
3326
0
            ImGuiTableColumn* column = &table->Columns[column_n];
3327
0
            if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
3328
0
                continue;
3329
0
            ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 };
3330
0
            temp_data->AngledHeadersRequests.push_back(request);
3331
0
        }
3332
3333
    // Render row
3334
0
    TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size);
3335
0
}
3336
3337
// Important: data must be fed left to right
3338
void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count)
3339
0
{
3340
0
    ImGuiContext& g = *GImGui;
3341
0
    ImGuiTable* table = g.CurrentTable;
3342
0
    ImGuiWindow* window = g.CurrentWindow;
3343
0
    ImDrawList* draw_list = window->DrawList;
3344
0
    if (table == NULL)
3345
0
    {
3346
0
        IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
3347
0
        return;
3348
0
    }
3349
0
    IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
3350
3351
0
    if (max_label_width == 0.0f)
3352
0
        max_label_width = TableGetHeaderAngledMaxLabelWidth();
3353
3354
    // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user.
3355
0
    const bool flip_label = (angle < 0.0f);
3356
0
    angle -= IM_PI * 0.5f;
3357
0
    const float cos_a = ImCos(angle);
3358
0
    const float sin_a = ImSin(angle);
3359
0
    const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a;
3360
0
    const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a;
3361
0
    const ImVec2 unit_right = ImVec2(cos_a, sin_a);
3362
3363
    // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
3364
    // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
3365
0
    const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f;
3366
0
    const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y));
3367
0
    table->AngledHeadersHeight = row_height;
3368
0
    table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;
3369
0
    const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right
3370
3371
    // Declare row, override and draw our own background
3372
0
    TableNextRow(ImGuiTableRowFlags_Headers, row_height);
3373
0
    TableNextColumn();
3374
0
    const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, table->RowPosY2);
3375
0
    table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0);
3376
0
    float clip_rect_min_x = table->BgClipRect.Min.x;
3377
0
    if (table->FreezeColumnsCount > 0)
3378
0
        clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX);
3379
0
    TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel
3380
0
    PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns
3381
0
    draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.
3382
0
    PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns
3383
3384
0
    ButtonBehavior(row_r, row_id, NULL, NULL);
3385
0
    KeepAliveID(row_id);
3386
3387
0
    const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
3388
0
    const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
3389
0
    const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
3390
0
    const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
3391
3392
    // Draw background and labels in first pass, then all borders.
3393
0
    float max_x = -FLT_MAX;
3394
0
    for (int pass = 0; pass < 2; pass++)
3395
0
        for (int order_n = 0; order_n < data_count; order_n++)
3396
0
        {
3397
0
            const ImGuiTableHeaderData* request = &data[order_n];
3398
0
            const int column_n = request->Index;
3399
0
            ImGuiTableColumn* column = &table->Columns[column_n];
3400
3401
0
            ImVec2 bg_shape[4];
3402
0
            bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);
3403
0
            bg_shape[1] = ImVec2(column->MinX, row_r.Max.y);
3404
0
            bg_shape[2] = bg_shape[1] + header_angled_vector;
3405
0
            bg_shape[3] = bg_shape[0] + header_angled_vector;
3406
0
            if (pass == 0)
3407
0
            {
3408
                // Draw shape
3409
0
                draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);
3410
0
                draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight
3411
0
                max_x = ImMax(max_x, bg_shape[3].x);
3412
3413
                // Draw label
3414
                // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset.
3415
                // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
3416
0
                const char* label_name = TableGetColumnName(table, column_n);
3417
0
                const char* label_name_end = FindRenderedTextEnd(label_name);
3418
0
                const float line_off_step_x = (g.FontSize / -sin_a);
3419
0
                const int label_lines = ImTextCountLines(label_name, label_name_end);
3420
3421
                // Left<>Right alignment
3422
0
                float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
3423
0
                float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
3424
0
                line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
3425
3426
                // Register header width
3427
0
                column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
3428
3429
0
                while (label_name < label_name_end)
3430
0
                {
3431
0
                    const char* label_name_eol = strchr(label_name, '\n');
3432
0
                    if (label_name_eol == NULL)
3433
0
                        label_name_eol = label_name_end;
3434
3435
                    // FIXME: Individual line clipping for right-most column is broken for negative angles.
3436
0
                    ImVec2 label_size = CalcTextSize(label_name, label_name_eol);
3437
0
                    float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
3438
0
                    float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
3439
0
                    ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
3440
0
                    int vtx_idx_begin = draw_list->_VtxCurrentIdx;
3441
0
                    PushStyleColor(ImGuiCol_Text, request->TextColor);
3442
0
                    RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
3443
0
                    PopStyleColor();
3444
0
                    int vtx_idx_end = draw_list->_VtxCurrentIdx;
3445
3446
                    // Up<>Down alignment
3447
0
                    const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f);
3448
0
                    const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f);
3449
3450
                    // Rotate and offset label
3451
0
                    ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y);
3452
0
                    ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y);
3453
0
                    line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x;
3454
0
                    pivot_out += unit_right * padding.y;
3455
0
                    if (flip_label)
3456
0
                        pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x));
3457
0
                    pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x;
3458
0
                    ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset
3459
                    //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
3460
3461
0
                    label_name = label_name_eol + 1;
3462
0
                }
3463
0
            }
3464
0
            if (pass == 1)
3465
0
            {
3466
                // Draw border
3467
0
                draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n));
3468
0
            }
3469
0
        }
3470
0
    PopClipRect();
3471
0
    PopClipRect();
3472
0
    table->TempData->AngledHeadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX);
3473
0
}
3474
3475
//-------------------------------------------------------------------------
3476
// [SECTION] Tables: Context Menu
3477
//-------------------------------------------------------------------------
3478
// - TableOpenContextMenu() [Internal]
3479
// - TableBeginContextMenuPopup() [Internal]
3480
// - TableDrawDefaultContextMenu() [Internal]
3481
//-------------------------------------------------------------------------
3482
3483
// Use -1 to open menu not specific to a given column.
3484
void ImGui::TableOpenContextMenu(int column_n)
3485
0
{
3486
0
    ImGuiContext& g = *GImGui;
3487
0
    ImGuiTable* table = g.CurrentTable;
3488
0
    if (column_n == -1 && table->CurrentColumn != -1)   // When called within a column automatically use this one (for consistency)
3489
0
        column_n = table->CurrentColumn;
3490
0
    if (column_n == table->ColumnsCount)                // To facilitate using with TableGetHoveredColumn()
3491
0
        column_n = -1;
3492
0
    IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount);
3493
0
    if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))
3494
0
    {
3495
0
        table->IsContextPopupOpen = true;
3496
0
        table->ContextPopupColumn = (ImGuiTableColumnIdx)column_n;
3497
0
        table->InstanceInteracted = table->InstanceCurrent;
3498
0
        const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID);
3499
0
        OpenPopupEx(context_menu_id, ImGuiPopupFlags_None);
3500
0
    }
3501
0
}
3502
3503
bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table)
3504
0
{
3505
0
    if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted)
3506
0
        return false;
3507
0
    const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID);
3508
0
    if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings))
3509
0
        return true;
3510
0
    table->IsContextPopupOpen = false;
3511
0
    return false;
3512
0
}
3513
3514
// Output context menu into current window (generally a popup)
3515
// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?
3516
// Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags.
3517
// - ImGuiTableFlags_Resizable   -> display Sizing menu items
3518
// - ImGuiTableFlags_Reorderable -> display "Reset Order"
3519
////- ImGuiTableFlags_Sortable   -> display sorting options (disabled)
3520
// - ImGuiTableFlags_Hideable    -> display columns visibility menu items
3521
// It means if you have a custom context menus you can call this section and omit some sections, and add your own.
3522
void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display)
3523
0
{
3524
0
    ImGuiContext& g = *GImGui;
3525
0
    ImGuiWindow* window = g.CurrentWindow;
3526
0
    if (window->SkipItems)
3527
0
        return;
3528
3529
0
    bool want_separator = false;
3530
0
    const int column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1;
3531
0
    ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL;
3532
3533
    // Sizing
3534
0
    if (flags_for_section_to_display & ImGuiTableFlags_Resizable)
3535
0
    {
3536
0
        if (column != NULL)
3537
0
        {
3538
0
            const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled;
3539
0
            if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // "###SizeOne"
3540
0
                TableSetColumnWidthAutoSingle(table, column_n);
3541
0
        }
3542
3543
0
        const char* size_all_desc;
3544
0
        if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame)
3545
0
            size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit);        // "###SizeAll" All fixed
3546
0
        else
3547
0
            size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault);    // "###SizeAll" All stretch or mixed
3548
0
        if (MenuItem(size_all_desc, NULL))
3549
0
            TableSetColumnWidthAutoAll(table);
3550
0
        want_separator = true;
3551
0
    }
3552
3553
    // Ordering
3554
0
    if (flags_for_section_to_display & ImGuiTableFlags_Reorderable)
3555
0
    {
3556
0
        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder))
3557
0
            table->IsResetDisplayOrderRequest = true;
3558
0
        want_separator = true;
3559
0
    }
3560
3561
    // Reset all (should work but seems unnecessary/noisy to expose?)
3562
    //if (MenuItem("Reset all"))
3563
    //    table->IsResetAllRequest = true;
3564
3565
    // Sorting
3566
    // (modify TableOpenContextMenu() to add _Sortable flag if enabling this)
3567
#if 0
3568
    if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0)
3569
    {
3570
        if (want_separator)
3571
            Separator();
3572
        want_separator = true;
3573
3574
        bool append_to_sort_specs = g.IO.KeyShift;
3575
        if (MenuItem("Sort in Ascending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Ascending, (column->Flags & ImGuiTableColumnFlags_NoSortAscending) == 0))
3576
            TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Ascending, append_to_sort_specs);
3577
        if (MenuItem("Sort in Descending Order", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Descending, (column->Flags & ImGuiTableColumnFlags_NoSortDescending) == 0))
3578
            TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Descending, append_to_sort_specs);
3579
    }
3580
#endif
3581
3582
    // Hiding / Visibility
3583
0
    if (flags_for_section_to_display & ImGuiTableFlags_Hideable)
3584
0
    {
3585
0
        if (want_separator)
3586
0
            Separator();
3587
0
        want_separator = true;
3588
3589
0
        PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
3590
0
        for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
3591
0
        {
3592
0
            ImGuiTableColumn* other_column = &table->Columns[other_column_n];
3593
0
            if (other_column->Flags & ImGuiTableColumnFlags_Disabled)
3594
0
                continue;
3595
3596
0
            const char* name = TableGetColumnName(table, other_column_n);
3597
0
            if (name == NULL || name[0] == 0)
3598
0
                name = "<Unknown>";
3599
3600
            // Make sure we can't hide the last active column
3601
0
            bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true;
3602
0
            if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1)
3603
0
                menu_item_active = false;
3604
0
            if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active))
3605
0
                other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled;
3606
0
        }
3607
0
        PopItemFlag();
3608
0
    }
3609
0
}
3610
3611
//-------------------------------------------------------------------------
3612
// [SECTION] Tables: Settings (.ini data)
3613
//-------------------------------------------------------------------------
3614
// FIXME: The binding/finding/creating flow are too confusing.
3615
//-------------------------------------------------------------------------
3616
// - TableSettingsInit() [Internal]
3617
// - TableSettingsCalcChunkSize() [Internal]
3618
// - TableSettingsCreate() [Internal]
3619
// - TableSettingsFindByID() [Internal]
3620
// - TableGetBoundSettings() [Internal]
3621
// - TableResetSettings()
3622
// - TableSaveSettings() [Internal]
3623
// - TableLoadSettings() [Internal]
3624
// - TableSettingsHandler_ClearAll() [Internal]
3625
// - TableSettingsHandler_ApplyAll() [Internal]
3626
// - TableSettingsHandler_ReadOpen() [Internal]
3627
// - TableSettingsHandler_ReadLine() [Internal]
3628
// - TableSettingsHandler_WriteAll() [Internal]
3629
// - TableSettingsInstallHandler() [Internal]
3630
//-------------------------------------------------------------------------
3631
// [Init] 1: TableSettingsHandler_ReadXXXX()   Load and parse .ini file into TableSettings.
3632
// [Main] 2: TableLoadSettings()               When table is created, bind Table to TableSettings, serialize TableSettings data into Table.
3633
// [Main] 3: TableSaveSettings()               When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty.
3634
// [Main] 4: TableSettingsHandler_WriteAll()   When .ini file is dirty (which can come from other source), save TableSettings into .ini file.
3635
//-------------------------------------------------------------------------
3636
3637
// Clear and initialize empty settings instance
3638
static void TableSettingsInit(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max)
3639
26
{
3640
26
    IM_PLACEMENT_NEW(settings) ImGuiTableSettings();
3641
26
    ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings();
3642
130
    for (int n = 0; n < columns_count_max; n++, settings_column++)
3643
104
        IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings();
3644
26
    settings->ID = id;
3645
26
    settings->ColumnsCount = (ImGuiTableColumnIdx)columns_count;
3646
26
    settings->ColumnsCountMax = (ImGuiTableColumnIdx)columns_count_max;
3647
26
    settings->WantApply = true;
3648
26
}
3649
3650
static size_t TableSettingsCalcChunkSize(int columns_count)
3651
26
{
3652
26
    return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings);
3653
26
}
3654
3655
ImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count)
3656
26
{
3657
26
    ImGuiContext& g = *GImGui;
3658
26
    ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count));
3659
26
    TableSettingsInit(settings, id, columns_count, columns_count);
3660
26
    return settings;
3661
26
}
3662
3663
// Find existing settings
3664
ImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)
3665
26
{
3666
    // FIXME-OPT: Might want to store a lookup map for this?
3667
26
    ImGuiContext& g = *GImGui;
3668
351
    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
3669
325
        if (settings->ID == id)
3670
0
            return settings;
3671
26
    return NULL;
3672
26
}
3673
3674
// Get settings for a given table, NULL if none
3675
ImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)
3676
0
{
3677
0
    if (table->SettingsOffset != -1)
3678
0
    {
3679
0
        ImGuiContext& g = *GImGui;
3680
0
        ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);
3681
0
        IM_ASSERT(settings->ID == table->ID);
3682
0
        if (settings->ColumnsCountMax >= table->ColumnsCount)
3683
0
            return settings; // OK
3684
0
        settings->ID = 0; // Invalidate storage, we won't fit because of a count change
3685
0
    }
3686
0
    return NULL;
3687
0
}
3688
3689
// Restore initial state of table (with or without saved settings)
3690
void ImGui::TableResetSettings(ImGuiTable* table)
3691
0
{
3692
0
    table->IsInitializing = table->IsSettingsDirty = true;
3693
0
    table->IsResetAllRequest = false;
3694
0
    table->IsSettingsRequestLoad = false;                   // Don't reload from ini
3695
0
    table->SettingsLoadedFlags = ImGuiTableFlags_None;      // Mark as nothing loaded so our initialized data becomes authoritative
3696
0
}
3697
3698
void ImGui::TableSaveSettings(ImGuiTable* table)
3699
0
{
3700
0
    table->IsSettingsDirty = false;
3701
0
    if (table->Flags & ImGuiTableFlags_NoSavedSettings)
3702
0
        return;
3703
3704
    // Bind or create settings data
3705
0
    ImGuiContext& g = *GImGui;
3706
0
    ImGuiTableSettings* settings = TableGetBoundSettings(table);
3707
0
    if (settings == NULL)
3708
0
    {
3709
0
        settings = TableSettingsCreate(table->ID, table->ColumnsCount);
3710
0
        table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);
3711
0
    }
3712
0
    settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount;
3713
3714
    // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings
3715
0
    IM_ASSERT(settings->ID == table->ID);
3716
0
    IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount);
3717
0
    ImGuiTableColumn* column = table->Columns.Data;
3718
0
    ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
3719
3720
0
    bool save_ref_scale = false;
3721
0
    settings->SaveFlags = ImGuiTableFlags_None;
3722
0
    for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++)
3723
0
    {
3724
0
        const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest;
3725
0
        column_settings->WidthOrWeight = width_or_weight;
3726
0
        column_settings->Index = (ImGuiTableColumnIdx)n;
3727
0
        column_settings->DisplayOrder = column->DisplayOrder;
3728
0
        column_settings->SortOrder = column->SortOrder;
3729
0
        column_settings->SortDirection = column->SortDirection;
3730
0
        column_settings->IsEnabled = column->IsUserEnabled;
3731
0
        column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0;
3732
0
        if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0)
3733
0
            save_ref_scale = true;
3734
3735
        // We skip saving some data in the .ini file when they are unnecessary to restore our state.
3736
        // Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f.
3737
        // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present.
3738
0
        if (width_or_weight != column->InitStretchWeightOrWidth)
3739
0
            settings->SaveFlags |= ImGuiTableFlags_Resizable;
3740
0
        if (column->DisplayOrder != n)
3741
0
            settings->SaveFlags |= ImGuiTableFlags_Reorderable;
3742
0
        if (column->SortOrder != -1)
3743
0
            settings->SaveFlags |= ImGuiTableFlags_Sortable;
3744
0
        if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0))
3745
0
            settings->SaveFlags |= ImGuiTableFlags_Hideable;
3746
0
    }
3747
0
    settings->SaveFlags &= table->Flags;
3748
0
    settings->RefScale = save_ref_scale ? table->RefScale : 0.0f;
3749
3750
0
    MarkIniSettingsDirty();
3751
0
}
3752
3753
void ImGui::TableLoadSettings(ImGuiTable* table)
3754
0
{
3755
0
    ImGuiContext& g = *GImGui;
3756
0
    table->IsSettingsRequestLoad = false;
3757
0
    if (table->Flags & ImGuiTableFlags_NoSavedSettings)
3758
0
        return;
3759
3760
    // Bind settings
3761
0
    ImGuiTableSettings* settings;
3762
0
    if (table->SettingsOffset == -1)
3763
0
    {
3764
0
        settings = TableSettingsFindByID(table->ID);
3765
0
        if (settings == NULL)
3766
0
            return;
3767
0
        if (settings->ColumnsCount != table->ColumnsCount) // Allow settings if columns count changed. We could otherwise decide to return...
3768
0
            table->IsSettingsDirty = true;
3769
0
        table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);
3770
0
    }
3771
0
    else
3772
0
    {
3773
0
        settings = TableGetBoundSettings(table);
3774
0
    }
3775
3776
0
    table->SettingsLoadedFlags = settings->SaveFlags;
3777
0
    table->RefScale = settings->RefScale;
3778
3779
    // Initialize default columns settings
3780
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3781
0
    {
3782
0
        ImGuiTableColumn* column = &table->Columns[column_n];
3783
0
        TableInitColumnDefaults(table, column, ~0);
3784
0
        column->AutoFitQueue = 0x00;
3785
0
    }
3786
3787
    // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
3788
0
    ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
3789
0
    ImU64 display_order_mask = 0;
3790
0
    for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)
3791
0
    {
3792
0
        int column_n = column_settings->Index;
3793
0
        if (column_n < 0 || column_n >= table->ColumnsCount)
3794
0
            continue;
3795
3796
0
        ImGuiTableColumn* column = &table->Columns[column_n];
3797
0
        if (settings->SaveFlags & ImGuiTableFlags_Resizable)
3798
0
        {
3799
0
            if (column_settings->IsStretch)
3800
0
                column->StretchWeight = column_settings->WidthOrWeight;
3801
0
            else
3802
0
                column->WidthRequest = column_settings->WidthOrWeight;
3803
0
        }
3804
0
        if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
3805
0
            column->DisplayOrder = column_settings->DisplayOrder;
3806
0
        display_order_mask |= (ImU64)1 << column->DisplayOrder;
3807
0
        if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
3808
0
            column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
3809
0
        column->SortOrder = column_settings->SortOrder;
3810
0
        column->SortDirection = column_settings->SortDirection;
3811
0
    }
3812
3813
    // Validate and fix invalid display order data
3814
0
    const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1;
3815
0
    if (display_order_mask != expected_display_order_mask)
3816
0
        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3817
0
            table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n;
3818
3819
    // Rebuild index
3820
0
    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
3821
0
        table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;
3822
0
}
3823
3824
static void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
3825
0
{
3826
0
    ImGuiContext& g = *ctx;
3827
0
    for (int i = 0; i != g.Tables.GetMapSize(); i++)
3828
0
        if (ImGuiTable* table = g.Tables.TryGetMapData(i))
3829
0
            table->SettingsOffset = -1;
3830
0
    g.SettingsTables.clear();
3831
0
}
3832
3833
// Apply to existing windows (if any)
3834
static void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)
3835
1
{
3836
1
    ImGuiContext& g = *ctx;
3837
1
    for (int i = 0; i != g.Tables.GetMapSize(); i++)
3838
0
        if (ImGuiTable* table = g.Tables.TryGetMapData(i))
3839
0
        {
3840
0
            table->IsSettingsRequestLoad = true;
3841
0
            table->SettingsOffset = -1;
3842
0
        }
3843
1
}
3844
3845
static void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
3846
26
{
3847
26
    ImGuiID id = 0;
3848
26
    int columns_count = 0;
3849
26
    if (sscanf(name, "0x%08X,%d", &id, &columns_count) < 2)
3850
0
        return NULL;
3851
3852
26
    if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id))
3853
0
    {
3854
0
        if (settings->ColumnsCountMax >= columns_count)
3855
0
        {
3856
0
            TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle
3857
0
            return settings;
3858
0
        }
3859
0
        settings->ID = 0; // Invalidate storage, we won't fit because of a count change
3860
0
    }
3861
26
    return ImGui::TableSettingsCreate(id, columns_count);
3862
26
}
3863
3864
static void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)
3865
53
{
3866
    // "Column 0  UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
3867
53
    ImGuiTableSettings* settings = (ImGuiTableSettings*)entry;
3868
53
    float f = 0.0f;
3869
53
    int column_n = 0, r = 0, n = 0;
3870
3871
53
    if (sscanf(line, "RefScale=%f", &f) == 1) { settings->RefScale = f; return; }
3872
3873
27
    if (sscanf(line, "Column %d%n", &column_n, &r) == 1)
3874
26
    {
3875
26
        if (column_n < 0 || column_n >= settings->ColumnsCount)
3876
0
            return;
3877
26
        line = ImStrSkipBlank(line + r);
3878
26
        char c = 0;
3879
26
        ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n;
3880
26
        column->Index = (ImGuiTableColumnIdx)column_n;
3881
26
        if (sscanf(line, "UserID=0x%08X%n", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; }
3882
26
        if (sscanf(line, "Width=%d%n", &n, &r) == 1)            { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; }
3883
26
        if (sscanf(line, "Weight=%f%n", &f, &r) == 1)           { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; }
3884
26
        if (sscanf(line, "Visible=%d%n", &n, &r) == 1)          { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; }
3885
26
        if (sscanf(line, "Order=%d%n", &n, &r) == 1)            { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; }
3886
26
        if (sscanf(line, "Sort=%d%c%n", &n, &c, &r) == 2)       { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; }
3887
26
    }
3888
27
}
3889
3890
static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
3891
1
{
3892
1
    ImGuiContext& g = *ctx;
3893
27
    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
3894
26
    {
3895
26
        if (settings->ID == 0) // Skip ditched settings
3896
0
            continue;
3897
3898
        // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped
3899
        // (e.g. Order was unchanged)
3900
26
        const bool save_size    = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0;
3901
26
        const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
3902
26
        const bool save_order   = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
3903
26
        const bool save_sort    = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
3904
        // We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
3905
3906
26
        buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
3907
26
        buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
3908
26
        if (settings->RefScale != 0.0f)
3909
26
            buf->appendf("RefScale=%g\n", settings->RefScale);
3910
26
        ImGuiTableColumnSettings* column = settings->GetColumnSettings();
3911
130
        for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++)
3912
104
        {
3913
            // "Column 0  UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v"
3914
104
            bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);
3915
104
            if (!save_column)
3916
78
                continue;
3917
26
            buf->appendf("Column %-2d", column_n);
3918
26
            if (column->UserID != 0)                    { buf->appendf(" UserID=%08X", column->UserID); }
3919
26
            if (save_size && column->IsStretch)         { buf->appendf(" Weight=%.4f", column->WidthOrWeight); }
3920
26
            if (save_size && !column->IsStretch)        { buf->appendf(" Width=%d", (int)column->WidthOrWeight); }
3921
26
            if (save_visible)                           { buf->appendf(" Visible=%d", column->IsEnabled); }
3922
26
            if (save_order)                             { buf->appendf(" Order=%d", column->DisplayOrder); }
3923
26
            if (save_sort && column->SortOrder != -1)   { buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); }
3924
26
            buf->append("\n");
3925
26
        }
3926
26
        buf->append("\n");
3927
26
    }
3928
1
}
3929
3930
void ImGui::TableSettingsAddSettingsHandler()
3931
1
{
3932
1
    ImGuiSettingsHandler ini_handler;
3933
1
    ini_handler.TypeName = "Table";
3934
1
    ini_handler.TypeHash = ImHashStr("Table");
3935
1
    ini_handler.ClearAllFn = TableSettingsHandler_ClearAll;
3936
1
    ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;
3937
1
    ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;
3938
1
    ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;
3939
1
    ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;
3940
1
    AddSettingsHandler(&ini_handler);
3941
1
}
3942
3943
//-------------------------------------------------------------------------
3944
// [SECTION] Tables: Garbage Collection
3945
//-------------------------------------------------------------------------
3946
// - TableRemove() [Internal]
3947
// - TableGcCompactTransientBuffers() [Internal]
3948
// - TableGcCompactSettings() [Internal]
3949
//-------------------------------------------------------------------------
3950
3951
// Remove Table data (currently only used by TestEngine)
3952
void ImGui::TableRemove(ImGuiTable* table)
3953
0
{
3954
    //IMGUI_DEBUG_PRINT("TableRemove() id=0x%08X\n", table->ID);
3955
0
    ImGuiContext& g = *GImGui;
3956
0
    int table_idx = g.Tables.GetIndex(table);
3957
    //memset(table->RawData.Data, 0, table->RawData.size_in_bytes());
3958
    //memset(table, 0, sizeof(ImGuiTable));
3959
0
    g.Tables.Remove(table->ID, table);
3960
0
    g.TablesLastTimeActive[table_idx] = -1.0f;
3961
0
}
3962
3963
// Free up/compact internal Table buffers for when it gets unused
3964
void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)
3965
0
{
3966
    //IMGUI_DEBUG_PRINT("TableGcCompactTransientBuffers() id=0x%08X\n", table->ID);
3967
0
    ImGuiContext& g = *GImGui;
3968
0
    IM_ASSERT(table->MemoryCompacted == false);
3969
0
    table->SortSpecs.Specs = NULL;
3970
0
    table->SortSpecsMulti.clear();
3971
0
    table->IsSortSpecsDirty = true; // FIXME: In theory shouldn't have to leak into user performing a sort on resume.
3972
0
    table->ColumnsNames.clear();
3973
0
    table->MemoryCompacted = true;
3974
0
    for (int n = 0; n < table->ColumnsCount; n++)
3975
0
        table->Columns[n].NameOffset = -1;
3976
0
    g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f;
3977
0
}
3978
3979
void ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data)
3980
0
{
3981
0
    temp_data->DrawSplitter.ClearFreeMemory();
3982
0
    temp_data->LastTimeActive = -1.0f;
3983
0
}
3984
3985
// Compact and remove unused settings data (currently only used by TestEngine)
3986
void ImGui::TableGcCompactSettings()
3987
0
{
3988
0
    ImGuiContext& g = *GImGui;
3989
0
    int required_memory = 0;
3990
0
    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
3991
0
        if (settings->ID != 0)
3992
0
            required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount);
3993
0
    if (required_memory == g.SettingsTables.Buf.Size)
3994
0
        return;
3995
0
    ImChunkStream<ImGuiTableSettings> new_chunk_stream;
3996
0
    new_chunk_stream.Buf.reserve(required_memory);
3997
0
    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))
3998
0
        if (settings->ID != 0)
3999
0
            memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount));
4000
0
    g.SettingsTables.swap(new_chunk_stream);
4001
0
}
4002
4003
4004
//-------------------------------------------------------------------------
4005
// [SECTION] Tables: Debugging
4006
//-------------------------------------------------------------------------
4007
// - DebugNodeTable() [Internal]
4008
//-------------------------------------------------------------------------
4009
4010
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
4011
4012
static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy)
4013
0
{
4014
0
    sizing_policy &= ImGuiTableFlags_SizingMask_;
4015
0
    if (sizing_policy == ImGuiTableFlags_SizingFixedFit)    { return "FixedFit"; }
4016
0
    if (sizing_policy == ImGuiTableFlags_SizingFixedSame)   { return "FixedSame"; }
4017
0
    if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return "StretchProp"; }
4018
0
    if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return "StretchSame"; }
4019
0
    return "N/A";
4020
0
}
4021
4022
void ImGui::DebugNodeTable(ImGuiTable* table)
4023
0
{
4024
0
    ImGuiContext& g = *GImGui;
4025
0
    const bool is_active = (table->LastFrameActive >= g.FrameCount - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.
4026
0
    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
4027
0
    bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*");
4028
0
    if (!is_active) { PopStyleColor(); }
4029
0
    if (IsItemHovered())
4030
0
        GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));
4031
0
    if (IsItemVisible() && table->HoveredColumnBody != -1)
4032
0
        GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));
4033
0
    if (!open)
4034
0
        return;
4035
0
    if (table->InstanceCurrent > 0)
4036
0
        Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1);
4037
0
    if (g.IO.ConfigDebugIsDebuggerPresent)
4038
0
    {
4039
0
        if (DebugBreakButton("**DebugBreak**", "in BeginTable()"))
4040
0
            g.DebugBreakInTable = table->ID;
4041
0
        SameLine();
4042
0
    }
4043
4044
0
    bool clear_settings = SmallButton("Clear settings");
4045
0
    BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags));
4046
0
    BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : "");
4047
0
    BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX);
4048
0
    BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder);
4049
0
    BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn);
4050
0
    for (int n = 0; n < table->InstanceCurrent + 1; n++)
4051
0
    {
4052
0
        ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n);
4053
0
        BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight);
4054
0
    }
4055
    //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen);
4056
0
    float sum_weights = 0.0f;
4057
0
    for (int n = 0; n < table->ColumnsCount; n++)
4058
0
        if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch)
4059
0
            sum_weights += table->Columns[n].StretchWeight;
4060
0
    for (int n = 0; n < table->ColumnsCount; n++)
4061
0
    {
4062
0
        ImGuiTableColumn* column = &table->Columns[n];
4063
0
        const char* name = TableGetColumnName(table, n);
4064
0
        char buf[512];
4065
0
        ImFormatString(buf, IM_ARRAYSIZE(buf),
4066
0
            "Column %d order %d '%s': offset %+.2f to %+.2f%s\n"
4067
0
            "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n"
4068
0
            "WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\n"
4069
0
            "MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\n"
4070
0
            "ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\n"
4071
0
            "Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..",
4072
0
            n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? " (Frozen)" : "",
4073
0
            column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen,
4074
0
            column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f,
4075
0
            column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x,
4076
0
            column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX,
4077
0
            column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? " (Asc)" : (column->SortDirection == ImGuiSortDirection_Descending) ? " (Des)" : "", column->UserID, column->Flags,
4078
0
            (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? "WidthStretch " : "",
4079
0
            (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? "WidthFixed " : "",
4080
0
            (column->Flags & ImGuiTableColumnFlags_NoResize) ? "NoResize " : "");
4081
0
        Bullet();
4082
0
        Selectable(buf);
4083
0
        if (IsItemHovered())
4084
0
        {
4085
0
            ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y);
4086
0
            GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));
4087
0
        }
4088
0
    }
4089
0
    if (ImGuiTableSettings* settings = TableGetBoundSettings(table))
4090
0
        DebugNodeTableSettings(settings);
4091
0
    if (clear_settings)
4092
0
        table->IsResetAllRequest = true;
4093
0
    TreePop();
4094
0
}
4095
4096
void ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings)
4097
0
{
4098
0
    if (!TreeNode((void*)(intptr_t)settings->ID, "Settings 0x%08X (%d columns)", settings->ID, settings->ColumnsCount))
4099
0
        return;
4100
0
    BulletText("SaveFlags: 0x%08X", settings->SaveFlags);
4101
0
    BulletText("ColumnsCount: %d (max %d)", settings->ColumnsCount, settings->ColumnsCountMax);
4102
0
    for (int n = 0; n < settings->ColumnsCount; n++)
4103
0
    {
4104
0
        ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];
4105
0
        ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;
4106
0
        BulletText("Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X",
4107
0
            n, column_settings->DisplayOrder, column_settings->SortOrder,
4108
0
            (sort_dir == ImGuiSortDirection_Ascending) ? "Asc" : (sort_dir == ImGuiSortDirection_Descending) ? "Des" : "---",
4109
0
            column_settings->IsEnabled, column_settings->IsStretch ? "Weight" : "Width ", column_settings->WidthOrWeight, column_settings->UserID);
4110
0
    }
4111
0
    TreePop();
4112
0
}
4113
4114
#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS
4115
4116
void ImGui::DebugNodeTable(ImGuiTable*) {}
4117
void ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}
4118
4119
#endif
4120
4121
4122
//-------------------------------------------------------------------------
4123
// [SECTION] Columns, BeginColumns, EndColumns, etc.
4124
// (This is a legacy API, prefer using BeginTable/EndTable!)
4125
//-------------------------------------------------------------------------
4126
// FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.)
4127
//-------------------------------------------------------------------------
4128
// - SetWindowClipRectBeforeSetChannel() [Internal]
4129
// - GetColumnIndex()
4130
// - GetColumnsCount()
4131
// - GetColumnOffset()
4132
// - GetColumnWidth()
4133
// - SetColumnOffset()
4134
// - SetColumnWidth()
4135
// - PushColumnClipRect() [Internal]
4136
// - PushColumnsBackground() [Internal]
4137
// - PopColumnsBackground() [Internal]
4138
// - FindOrCreateColumns() [Internal]
4139
// - GetColumnsID() [Internal]
4140
// - BeginColumns()
4141
// - NextColumn()
4142
// - EndColumns()
4143
// - Columns()
4144
//-------------------------------------------------------------------------
4145
4146
// [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences,
4147
// they would meddle many times with the underlying ImDrawCmd.
4148
// Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let
4149
// the subsequent single call to SetCurrentChannel() does it things once.
4150
void ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect)
4151
0
{
4152
0
    ImVec4 clip_rect_vec4 = clip_rect.ToVec4();
4153
0
    window->ClipRect = clip_rect;
4154
0
    window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4;
4155
0
    window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4;
4156
0
}
4157
4158
int ImGui::GetColumnIndex()
4159
0
{
4160
0
    ImGuiWindow* window = GetCurrentWindowRead();
4161
0
    return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0;
4162
0
}
4163
4164
int ImGui::GetColumnsCount()
4165
0
{
4166
0
    ImGuiWindow* window = GetCurrentWindowRead();
4167
0
    return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1;
4168
0
}
4169
4170
float ImGui::GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm)
4171
0
{
4172
0
    return offset_norm * (columns->OffMaxX - columns->OffMinX);
4173
0
}
4174
4175
float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset)
4176
0
{
4177
0
    return offset / (columns->OffMaxX - columns->OffMinX);
4178
0
}
4179
4180
static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f;
4181
4182
static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
4183
0
{
4184
    // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing
4185
    // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.
4186
0
    ImGuiContext& g = *GImGui;
4187
0
    ImGuiWindow* window = g.CurrentWindow;
4188
0
    IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
4189
0
    IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
4190
4191
0
    float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x;
4192
0
    x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
4193
0
    if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))
4194
0
        x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
4195
4196
0
    return x;
4197
0
}
4198
4199
float ImGui::GetColumnOffset(int column_index)
4200
0
{
4201
0
    ImGuiWindow* window = GetCurrentWindowRead();
4202
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4203
0
    if (columns == NULL)
4204
0
        return 0.0f;
4205
4206
0
    if (column_index < 0)
4207
0
        column_index = columns->Current;
4208
0
    IM_ASSERT(column_index < columns->Columns.Size);
4209
4210
0
    const float t = columns->Columns[column_index].OffsetNorm;
4211
0
    const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t);
4212
0
    return x_offset;
4213
0
}
4214
4215
static float GetColumnWidthEx(ImGuiOldColumns* columns, int column_index, bool before_resize = false)
4216
0
{
4217
0
    if (column_index < 0)
4218
0
        column_index = columns->Current;
4219
4220
0
    float offset_norm;
4221
0
    if (before_resize)
4222
0
        offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;
4223
0
    else
4224
0
        offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;
4225
0
    return ImGui::GetColumnOffsetFromNorm(columns, offset_norm);
4226
0
}
4227
4228
float ImGui::GetColumnWidth(int column_index)
4229
0
{
4230
0
    ImGuiContext& g = *GImGui;
4231
0
    ImGuiWindow* window = g.CurrentWindow;
4232
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4233
0
    if (columns == NULL)
4234
0
        return GetContentRegionAvail().x;
4235
4236
0
    if (column_index < 0)
4237
0
        column_index = columns->Current;
4238
0
    return GetColumnOffsetFromNorm(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);
4239
0
}
4240
4241
void ImGui::SetColumnOffset(int column_index, float offset)
4242
0
{
4243
0
    ImGuiContext& g = *GImGui;
4244
0
    ImGuiWindow* window = g.CurrentWindow;
4245
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4246
0
    IM_ASSERT(columns != NULL);
4247
4248
0
    if (column_index < 0)
4249
0
        column_index = columns->Current;
4250
0
    IM_ASSERT(column_index < columns->Columns.Size);
4251
4252
0
    const bool preserve_width = !(columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths) && (column_index < columns->Count - 1);
4253
0
    const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;
4254
4255
0
    if (!(columns->Flags & ImGuiOldColumnFlags_NoForceWithinWindow))
4256
0
        offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));
4257
0
    columns->Columns[column_index].OffsetNorm = GetColumnNormFromOffset(columns, offset - columns->OffMinX);
4258
4259
0
    if (preserve_width)
4260
0
        SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));
4261
0
}
4262
4263
void ImGui::SetColumnWidth(int column_index, float width)
4264
0
{
4265
0
    ImGuiWindow* window = GetCurrentWindowRead();
4266
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4267
0
    IM_ASSERT(columns != NULL);
4268
4269
0
    if (column_index < 0)
4270
0
        column_index = columns->Current;
4271
0
    SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);
4272
0
}
4273
4274
void ImGui::PushColumnClipRect(int column_index)
4275
0
{
4276
0
    ImGuiWindow* window = GetCurrentWindowRead();
4277
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4278
0
    if (column_index < 0)
4279
0
        column_index = columns->Current;
4280
4281
0
    ImGuiOldColumnData* column = &columns->Columns[column_index];
4282
0
    PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false);
4283
0
}
4284
4285
// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns)
4286
void ImGui::PushColumnsBackground()
4287
0
{
4288
0
    ImGuiWindow* window = GetCurrentWindowRead();
4289
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4290
0
    if (columns->Count == 1)
4291
0
        return;
4292
4293
    // Optimization: avoid SetCurrentChannel() + PushClipRect()
4294
0
    columns->HostBackupClipRect = window->ClipRect;
4295
0
    SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect);
4296
0
    columns->Splitter.SetCurrentChannel(window->DrawList, 0);
4297
0
}
4298
4299
void ImGui::PopColumnsBackground()
4300
0
{
4301
0
    ImGuiWindow* window = GetCurrentWindowRead();
4302
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4303
0
    if (columns->Count == 1)
4304
0
        return;
4305
4306
    // Optimization: avoid PopClipRect() + SetCurrentChannel()
4307
0
    SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect);
4308
0
    columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);
4309
0
}
4310
4311
ImGuiOldColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id)
4312
0
{
4313
    // We have few columns per window so for now we don't need bother much with turning this into a faster lookup.
4314
0
    for (int n = 0; n < window->ColumnsStorage.Size; n++)
4315
0
        if (window->ColumnsStorage[n].ID == id)
4316
0
            return &window->ColumnsStorage[n];
4317
4318
0
    window->ColumnsStorage.push_back(ImGuiOldColumns());
4319
0
    ImGuiOldColumns* columns = &window->ColumnsStorage.back();
4320
0
    columns->ID = id;
4321
0
    return columns;
4322
0
}
4323
4324
ImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count)
4325
0
{
4326
0
    ImGuiWindow* window = GetCurrentWindow();
4327
4328
    // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.
4329
    // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.
4330
0
    PushID(0x11223347 + (str_id ? 0 : columns_count));
4331
0
    ImGuiID id = window->GetID(str_id ? str_id : "columns");
4332
0
    PopID();
4333
4334
0
    return id;
4335
0
}
4336
4337
void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFlags flags)
4338
0
{
4339
0
    ImGuiContext& g = *GImGui;
4340
0
    ImGuiWindow* window = GetCurrentWindow();
4341
4342
0
    IM_ASSERT(columns_count >= 1);
4343
0
    IM_ASSERT(window->DC.CurrentColumns == NULL);   // Nested columns are currently not supported
4344
4345
    // Acquire storage for the columns set
4346
0
    ImGuiID id = GetColumnsID(str_id, columns_count);
4347
0
    ImGuiOldColumns* columns = FindOrCreateColumns(window, id);
4348
0
    IM_ASSERT(columns->ID == id);
4349
0
    columns->Current = 0;
4350
0
    columns->Count = columns_count;
4351
0
    columns->Flags = flags;
4352
0
    window->DC.CurrentColumns = columns;
4353
0
    window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();
4354
4355
0
    columns->HostCursorPosY = window->DC.CursorPos.y;
4356
0
    columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x;
4357
0
    columns->HostInitialClipRect = window->ClipRect;
4358
0
    columns->HostBackupParentWorkRect = window->ParentWorkRect;
4359
0
    window->ParentWorkRect = window->WorkRect;
4360
4361
    // Set state for first column
4362
    // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect
4363
0
    const float column_padding = g.Style.ItemSpacing.x;
4364
0
    const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize));
4365
0
    const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f);
4366
0
    const float max_2 = window->WorkRect.Max.x + half_clip_extend_x;
4367
0
    columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f);
4368
0
    columns->OffMaxX = ImMax(ImMin(max_1, max_2) - window->Pos.x, columns->OffMinX + 1.0f);
4369
0
    columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;
4370
4371
    // Clear data if columns count changed
4372
0
    if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)
4373
0
        columns->Columns.resize(0);
4374
4375
    // Initialize default widths
4376
0
    columns->IsFirstFrame = (columns->Columns.Size == 0);
4377
0
    if (columns->Columns.Size == 0)
4378
0
    {
4379
0
        columns->Columns.reserve(columns_count + 1);
4380
0
        for (int n = 0; n < columns_count + 1; n++)
4381
0
        {
4382
0
            ImGuiOldColumnData column;
4383
0
            column.OffsetNorm = n / (float)columns_count;
4384
0
            columns->Columns.push_back(column);
4385
0
        }
4386
0
    }
4387
4388
0
    for (int n = 0; n < columns_count; n++)
4389
0
    {
4390
        // Compute clipping rectangle
4391
0
        ImGuiOldColumnData* column = &columns->Columns[n];
4392
0
        float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n));
4393
0
        float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f);
4394
0
        column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);
4395
0
        column->ClipRect.ClipWithFull(window->ClipRect);
4396
0
    }
4397
4398
0
    if (columns->Count > 1)
4399
0
    {
4400
0
        columns->Splitter.Split(window->DrawList, 1 + columns->Count);
4401
0
        columns->Splitter.SetCurrentChannel(window->DrawList, 1);
4402
0
        PushColumnClipRect(0);
4403
0
    }
4404
4405
    // We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user.
4406
0
    float offset_0 = GetColumnOffset(columns->Current);
4407
0
    float offset_1 = GetColumnOffset(columns->Current + 1);
4408
0
    float width = offset_1 - offset_0;
4409
0
    PushItemWidth(width * 0.65f);
4410
0
    window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);
4411
0
    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
4412
0
    window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;
4413
0
    window->WorkRect.Max.y = window->ContentRegionRect.Max.y;
4414
0
}
4415
4416
void ImGui::NextColumn()
4417
0
{
4418
0
    ImGuiWindow* window = GetCurrentWindow();
4419
0
    if (window->SkipItems || window->DC.CurrentColumns == NULL)
4420
0
        return;
4421
4422
0
    ImGuiContext& g = *GImGui;
4423
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4424
4425
0
    if (columns->Count == 1)
4426
0
    {
4427
0
        window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
4428
0
        IM_ASSERT(columns->Current == 0);
4429
0
        return;
4430
0
    }
4431
4432
    // Next column
4433
0
    if (++columns->Current == columns->Count)
4434
0
        columns->Current = 0;
4435
4436
0
    PopItemWidth();
4437
4438
    // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect()
4439
    // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them),
4440
0
    ImGuiOldColumnData* column = &columns->Columns[columns->Current];
4441
0
    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
4442
0
    columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);
4443
4444
0
    const float column_padding = g.Style.ItemSpacing.x;
4445
0
    columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
4446
0
    if (columns->Current > 0)
4447
0
    {
4448
        // Columns 1+ ignore IndentX (by canceling it out)
4449
        // FIXME-COLUMNS: Unnecessary, could be locked?
4450
0
        window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding;
4451
0
    }
4452
0
    else
4453
0
    {
4454
        // New row/line: column 0 honor IndentX.
4455
0
        window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);
4456
0
        window->DC.IsSameLine = false;
4457
0
        columns->LineMinY = columns->LineMaxY;
4458
0
    }
4459
0
    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
4460
0
    window->DC.CursorPos.y = columns->LineMinY;
4461
0
    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
4462
0
    window->DC.CurrLineTextBaseOffset = 0.0f;
4463
4464
    // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup.
4465
0
    float offset_0 = GetColumnOffset(columns->Current);
4466
0
    float offset_1 = GetColumnOffset(columns->Current + 1);
4467
0
    float width = offset_1 - offset_0;
4468
0
    PushItemWidth(width * 0.65f);
4469
0
    window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;
4470
0
}
4471
4472
void ImGui::EndColumns()
4473
0
{
4474
0
    ImGuiContext& g = *GImGui;
4475
0
    ImGuiWindow* window = GetCurrentWindow();
4476
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4477
0
    IM_ASSERT(columns != NULL);
4478
4479
0
    PopItemWidth();
4480
0
    if (columns->Count > 1)
4481
0
    {
4482
0
        PopClipRect();
4483
0
        columns->Splitter.Merge(window->DrawList);
4484
0
    }
4485
4486
0
    const ImGuiOldColumnFlags flags = columns->Flags;
4487
0
    columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);
4488
0
    window->DC.CursorPos.y = columns->LineMaxY;
4489
0
    if (!(flags & ImGuiOldColumnFlags_GrowParentContentsSize))
4490
0
        window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX;  // Restore cursor max pos, as columns don't grow parent
4491
4492
    // Draw columns borders and handle resize
4493
    // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy
4494
0
    bool is_being_resized = false;
4495
0
    if (!(flags & ImGuiOldColumnFlags_NoBorder) && !window->SkipItems)
4496
0
    {
4497
        // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.
4498
0
        const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y);
4499
0
        const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y);
4500
0
        int dragging_column = -1;
4501
0
        for (int n = 1; n < columns->Count; n++)
4502
0
        {
4503
0
            ImGuiOldColumnData* column = &columns->Columns[n];
4504
0
            float x = window->Pos.x + GetColumnOffset(n);
4505
0
            const ImGuiID column_id = columns->ID + ImGuiID(n);
4506
0
            const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale);
4507
0
            const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
4508
0
            if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
4509
0
                continue;
4510
4511
0
            bool hovered = false, held = false;
4512
0
            if (!(flags & ImGuiOldColumnFlags_NoResize))
4513
0
            {
4514
0
                ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
4515
0
                if (hovered || held)
4516
0
                    SetMouseCursor(ImGuiMouseCursor_ResizeEW);
4517
0
                if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
4518
0
                    dragging_column = n;
4519
0
            }
4520
4521
            // Draw column
4522
0
            const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
4523
0
            const float xi = IM_TRUNC(x);
4524
0
            window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);
4525
0
        }
4526
4527
        // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.
4528
0
        if (dragging_column != -1)
4529
0
        {
4530
0
            if (!columns->IsBeingResized)
4531
0
                for (int n = 0; n < columns->Count + 1; n++)
4532
0
                    columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;
4533
0
            columns->IsBeingResized = is_being_resized = true;
4534
0
            float x = GetDraggedColumnOffset(columns, dragging_column);
4535
0
            SetColumnOffset(dragging_column, x);
4536
0
        }
4537
0
    }
4538
0
    columns->IsBeingResized = is_being_resized;
4539
4540
0
    window->WorkRect = window->ParentWorkRect;
4541
0
    window->ParentWorkRect = columns->HostBackupParentWorkRect;
4542
0
    window->DC.CurrentColumns = NULL;
4543
0
    window->DC.ColumnsOffset.x = 0.0f;
4544
0
    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);
4545
0
    NavUpdateCurrentWindowIsScrollPushableX();
4546
0
}
4547
4548
void ImGui::Columns(int columns_count, const char* id, bool borders)
4549
0
{
4550
0
    ImGuiWindow* window = GetCurrentWindow();
4551
0
    IM_ASSERT(columns_count >= 1);
4552
4553
0
    ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
4554
    //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
4555
0
    ImGuiOldColumns* columns = window->DC.CurrentColumns;
4556
0
    if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)
4557
0
        return;
4558
4559
0
    if (columns != NULL)
4560
0
        EndColumns();
4561
4562
0
    if (columns_count != 1)
4563
0
        BeginColumns(id, columns_count, flags);
4564
0
}
4565
4566
//-------------------------------------------------------------------------
4567
4568
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imgui_widgets.cpp
Line
Count
Source
1
// dear imgui, v1.92.4
2
// (widgets code)
3
4
/*
5
6
Index of this file:
7
8
// [SECTION] Forward Declarations
9
// [SECTION] Widgets: Text, etc.
10
// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.)
11
// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.)
12
// [SECTION] Widgets: ComboBox
13
// [SECTION] Data Type and Data Formatting Helpers
14
// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
15
// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
16
// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
17
// [SECTION] Widgets: InputText, InputTextMultiline
18
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
19
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
20
// [SECTION] Widgets: Selectable
21
// [SECTION] Widgets: Typing-Select support
22
// [SECTION] Widgets: Box-Select support
23
// [SECTION] Widgets: Multi-Select support
24
// [SECTION] Widgets: Multi-Select helpers
25
// [SECTION] Widgets: ListBox
26
// [SECTION] Widgets: PlotLines, PlotHistogram
27
// [SECTION] Widgets: Value helpers
28
// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.
29
// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
30
// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
31
// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc.
32
33
*/
34
35
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
36
#define _CRT_SECURE_NO_WARNINGS
37
#endif
38
39
#ifndef IMGUI_DEFINE_MATH_OPERATORS
40
#define IMGUI_DEFINE_MATH_OPERATORS
41
#endif
42
43
#include "imgui.h"
44
#ifndef IMGUI_DISABLE
45
#include "imgui_internal.h"
46
47
// System includes
48
#include <stdint.h>     // intptr_t
49
50
//-------------------------------------------------------------------------
51
// Warnings
52
//-------------------------------------------------------------------------
53
54
// Visual Studio warnings
55
#ifdef _MSC_VER
56
#pragma warning (disable: 4127)     // condition expression is constant
57
#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
58
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
59
#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types
60
#endif
61
#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
62
#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
63
#endif
64
65
// Clang/GCC warnings with -Weverything
66
#if defined(__clang__)
67
#if __has_warning("-Wunknown-warning-option")
68
#pragma clang diagnostic ignored "-Wunknown-warning-option"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!
69
#endif
70
#pragma clang diagnostic ignored "-Wunknown-pragmas"                // warning: unknown warning group 'xxx'
71
#pragma clang diagnostic ignored "-Wold-style-cast"                 // warning: use of old-style cast                            // yes, they are more terse.
72
#pragma clang diagnostic ignored "-Wfloat-equal"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
73
#pragma clang diagnostic ignored "-Wformat"                         // warning: format specifies type 'int' but the argument has type 'unsigned int'
74
#pragma clang diagnostic ignored "-Wformat-nonliteral"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
75
#pragma clang diagnostic ignored "-Wsign-conversion"                // warning: implicit conversion changes signedness
76
#pragma clang diagnostic ignored "-Wunused-macros"                  // warning: macro is not used                                // we define snprintf/vsnprintf on Windows so they are available, but not always used.
77
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0
78
#pragma clang diagnostic ignored "-Wdouble-promotion"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
79
#pragma clang diagnostic ignored "-Wenum-enum-conversion"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
80
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
81
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion"  // warning: implicit conversion from 'xxx' to 'float' may lose precision
82
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"            // warning: 'xxx' is an unsafe pointer used for buffer access
83
#pragma clang diagnostic ignored "-Wnontrivial-memaccess"           // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
84
#pragma clang diagnostic ignored "-Wswitch-default"                 // warning: 'switch' missing 'default' label
85
#elif defined(__GNUC__)
86
#pragma GCC diagnostic ignored "-Wpragmas"                          // warning: unknown option after '#pragma GCC diagnostic' kind
87
#pragma GCC diagnostic ignored "-Wfloat-equal"                      // warning: comparing floating-point with '==' or '!=' is unsafe
88
#pragma GCC diagnostic ignored "-Wformat"                           // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
89
#pragma GCC diagnostic ignored "-Wformat-nonliteral"                // warning: format not a string literal, format string not checked
90
#pragma GCC diagnostic ignored "-Wdeprecated-enum-enum-conversion"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
91
#pragma GCC diagnostic ignored "-Wdouble-promotion"                 // warning: implicit conversion from 'float' to 'double' when passing argument to function
92
#pragma GCC diagnostic ignored "-Wstrict-overflow"                  // warning: assuming signed overflow does not occur when simplifying division / ..when changing X +- C1 cmp C2 to X cmp C2 -+ C1
93
#pragma GCC diagnostic ignored "-Wclass-memaccess"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
94
#pragma GCC diagnostic ignored "-Wcast-qual"                        // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers
95
#endif
96
97
//-------------------------------------------------------------------------
98
// Data
99
//-------------------------------------------------------------------------
100
101
// Widgets
102
static const float          DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f;    // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior.
103
static const float          DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f;    // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags.
104
105
// Those MIN/MAX values are not define because we need to point to them
106
static const signed char    IM_S8_MIN  = -128;
107
static const signed char    IM_S8_MAX  = 127;
108
static const unsigned char  IM_U8_MIN  = 0;
109
static const unsigned char  IM_U8_MAX  = 0xFF;
110
static const signed short   IM_S16_MIN = -32768;
111
static const signed short   IM_S16_MAX = 32767;
112
static const unsigned short IM_U16_MIN = 0;
113
static const unsigned short IM_U16_MAX = 0xFFFF;
114
static const ImS32          IM_S32_MIN = INT_MIN;    // (-2147483647 - 1), (0x80000000);
115
static const ImS32          IM_S32_MAX = INT_MAX;    // (2147483647), (0x7FFFFFFF)
116
static const ImU32          IM_U32_MIN = 0;
117
static const ImU32          IM_U32_MAX = UINT_MAX;   // (0xFFFFFFFF)
118
#ifdef LLONG_MIN
119
static const ImS64          IM_S64_MIN = LLONG_MIN;  // (-9223372036854775807ll - 1ll);
120
static const ImS64          IM_S64_MAX = LLONG_MAX;  // (9223372036854775807ll);
121
#else
122
static const ImS64          IM_S64_MIN = -9223372036854775807LL - 1;
123
static const ImS64          IM_S64_MAX = 9223372036854775807LL;
124
#endif
125
static const ImU64          IM_U64_MIN = 0;
126
#ifdef ULLONG_MAX
127
static const ImU64          IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull);
128
#else
129
static const ImU64          IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);
130
#endif
131
132
//-------------------------------------------------------------------------
133
// [SECTION] Forward Declarations
134
//-------------------------------------------------------------------------
135
136
// For InputTextEx()
137
static bool     InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard = false);
138
static ImVec2   InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining = NULL, ImVec2* out_offset = NULL, ImDrawTextFlags flags = 0);
139
140
//-------------------------------------------------------------------------
141
// [SECTION] Widgets: Text, etc.
142
//-------------------------------------------------------------------------
143
// - TextEx() [Internal]
144
// - TextUnformatted()
145
// - Text()
146
// - TextV()
147
// - TextColored()
148
// - TextColoredV()
149
// - TextDisabled()
150
// - TextDisabledV()
151
// - TextWrapped()
152
// - TextWrappedV()
153
// - LabelText()
154
// - LabelTextV()
155
// - BulletText()
156
// - BulletTextV()
157
//-------------------------------------------------------------------------
158
159
void ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)
160
0
{
161
0
    ImGuiWindow* window = GetCurrentWindow();
162
0
    if (window->SkipItems)
163
0
        return;
164
0
    ImGuiContext& g = *GImGui;
165
166
    // Accept null ranges
167
0
    if (text == text_end)
168
0
        text = text_end = "";
169
170
    // Calculate length
171
0
    const char* text_begin = text;
172
0
    if (text_end == NULL)
173
0
        text_end = text + ImStrlen(text); // FIXME-OPT
174
175
0
    const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
176
0
    const float wrap_pos_x = window->DC.TextWrapPos;
177
0
    const bool wrap_enabled = (wrap_pos_x >= 0.0f);
178
0
    if (text_end - text <= 2000 || wrap_enabled)
179
0
    {
180
        // Common case
181
0
        const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;
182
0
        const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
183
184
0
        ImRect bb(text_pos, text_pos + text_size);
185
0
        ItemSize(text_size, 0.0f);
186
0
        if (!ItemAdd(bb, 0))
187
0
            return;
188
189
        // Render (we don't hide text after ## in this end-user function)
190
0
        RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);
191
0
    }
192
0
    else
193
0
    {
194
        // Long text!
195
        // Perform manual coarse clipping to optimize for long multi-line text
196
        // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.
197
        // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.
198
        // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.
199
0
        const char* line = text;
200
0
        const float line_height = GetTextLineHeight();
201
0
        ImVec2 text_size(0, 0);
202
203
        // Lines to skip (can't skip when logging text)
204
0
        ImVec2 pos = text_pos;
205
0
        if (!g.LogEnabled)
206
0
        {
207
0
            int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height);
208
0
            if (lines_skippable > 0)
209
0
            {
210
0
                int lines_skipped = 0;
211
0
                while (line < text_end && lines_skipped < lines_skippable)
212
0
                {
213
0
                    const char* line_end = (const char*)ImMemchr(line, '\n', text_end - line);
214
0
                    if (!line_end)
215
0
                        line_end = text_end;
216
0
                    if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
217
0
                        text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
218
0
                    line = line_end + 1;
219
0
                    lines_skipped++;
220
0
                }
221
0
                pos.y += lines_skipped * line_height;
222
0
            }
223
0
        }
224
225
        // Lines to render
226
0
        if (line < text_end)
227
0
        {
228
0
            ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
229
0
            while (line < text_end)
230
0
            {
231
0
                if (IsClippedEx(line_rect, 0))
232
0
                    break;
233
234
0
                const char* line_end = (const char*)ImMemchr(line, '\n', text_end - line);
235
0
                if (!line_end)
236
0
                    line_end = text_end;
237
0
                text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
238
0
                RenderText(pos, line, line_end, false);
239
0
                line = line_end + 1;
240
0
                line_rect.Min.y += line_height;
241
0
                line_rect.Max.y += line_height;
242
0
                pos.y += line_height;
243
0
            }
244
245
            // Count remaining lines
246
0
            int lines_skipped = 0;
247
0
            while (line < text_end)
248
0
            {
249
0
                const char* line_end = (const char*)ImMemchr(line, '\n', text_end - line);
250
0
                if (!line_end)
251
0
                    line_end = text_end;
252
0
                if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)
253
0
                    text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);
254
0
                line = line_end + 1;
255
0
                lines_skipped++;
256
0
            }
257
0
            pos.y += lines_skipped * line_height;
258
0
        }
259
0
        text_size.y = (pos - text_pos).y;
260
261
0
        ImRect bb(text_pos, text_pos + text_size);
262
0
        ItemSize(text_size, 0.0f);
263
0
        ItemAdd(bb, 0);
264
0
    }
265
0
}
266
267
void ImGui::TextUnformatted(const char* text, const char* text_end)
268
0
{
269
0
    TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
270
0
}
271
272
void ImGui::Text(const char* fmt, ...)
273
0
{
274
0
    va_list args;
275
0
    va_start(args, fmt);
276
0
    TextV(fmt, args);
277
0
    va_end(args);
278
0
}
279
280
void ImGui::TextV(const char* fmt, va_list args)
281
0
{
282
0
    ImGuiWindow* window = GetCurrentWindow();
283
0
    if (window->SkipItems)
284
0
        return;
285
286
0
    const char* text, *text_end;
287
0
    ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
288
0
    TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);
289
0
}
290
291
void ImGui::TextColored(const ImVec4& col, const char* fmt, ...)
292
0
{
293
0
    va_list args;
294
0
    va_start(args, fmt);
295
0
    TextColoredV(col, fmt, args);
296
0
    va_end(args);
297
0
}
298
299
void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)
300
0
{
301
0
    PushStyleColor(ImGuiCol_Text, col);
302
0
    TextV(fmt, args);
303
0
    PopStyleColor();
304
0
}
305
306
void ImGui::TextDisabled(const char* fmt, ...)
307
0
{
308
0
    va_list args;
309
0
    va_start(args, fmt);
310
0
    TextDisabledV(fmt, args);
311
0
    va_end(args);
312
0
}
313
314
void ImGui::TextDisabledV(const char* fmt, va_list args)
315
0
{
316
0
    ImGuiContext& g = *GImGui;
317
0
    PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
318
0
    TextV(fmt, args);
319
0
    PopStyleColor();
320
0
}
321
322
void ImGui::TextWrapped(const char* fmt, ...)
323
0
{
324
0
    va_list args;
325
0
    va_start(args, fmt);
326
0
    TextWrappedV(fmt, args);
327
0
    va_end(args);
328
0
}
329
330
void ImGui::TextWrappedV(const char* fmt, va_list args)
331
0
{
332
0
    ImGuiContext& g = *GImGui;
333
0
    const bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f);  // Keep existing wrap position if one is already set
334
0
    if (need_backup)
335
0
        PushTextWrapPos(0.0f);
336
0
    TextV(fmt, args);
337
0
    if (need_backup)
338
0
        PopTextWrapPos();
339
0
}
340
341
void ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...)
342
0
{
343
0
    va_list args;
344
0
    va_start(args, fmt);
345
0
    TextAlignedV(align_x, size_x, fmt, args);
346
0
    va_end(args);
347
0
}
348
349
// align_x: 0.0f = left, 0.5f = center, 1.0f = right.
350
// size_x : 0.0f = shortcut for GetContentRegionAvail().x
351
// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024)
352
void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args)
353
0
{
354
0
    ImGuiWindow* window = GetCurrentWindow();
355
0
    if (window->SkipItems)
356
0
        return;
357
358
0
    const char* text, *text_end;
359
0
    ImFormatStringToTempBufferV(&text, &text_end, fmt, args);
360
0
    const ImVec2 text_size = CalcTextSize(text, text_end);
361
0
    size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x;
362
363
0
    ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
364
0
    ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y);
365
0
    ImVec2 size(ImMin(size_x, text_size.x), text_size.y);
366
0
    window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x);
367
0
    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x);
368
0
    if (align_x > 0.0f && text_size.x < size_x)
369
0
        pos.x += ImTrunc((size_x - text_size.x) * align_x);
370
0
    RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size);
371
372
0
    const ImVec2 backup_max_pos = window->DC.CursorMaxPos;
373
0
    ItemSize(size);
374
0
    ItemAdd(ImRect(pos, pos + size), 0);
375
0
    window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up.
376
377
0
    if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip))
378
0
        SetTooltip("%.*s", (int)(text_end - text), text);
379
0
}
380
381
void ImGui::LabelText(const char* label, const char* fmt, ...)
382
0
{
383
0
    va_list args;
384
0
    va_start(args, fmt);
385
0
    LabelTextV(label, fmt, args);
386
0
    va_end(args);
387
0
}
388
389
// Add a label+text combo aligned to other label+value widgets
390
void ImGui::LabelTextV(const char* label, const char* fmt, va_list args)
391
0
{
392
0
    ImGuiWindow* window = GetCurrentWindow();
393
0
    if (window->SkipItems)
394
0
        return;
395
396
0
    ImGuiContext& g = *GImGui;
397
0
    const ImGuiStyle& style = g.Style;
398
0
    const float w = CalcItemWidth();
399
400
0
    const char* value_text_begin, *value_text_end;
401
0
    ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args);
402
0
    const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false);
403
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
404
405
0
    const ImVec2 pos = window->DC.CursorPos;
406
0
    const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));
407
0
    const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2));
408
0
    ItemSize(total_bb, style.FramePadding.y);
409
0
    if (!ItemAdd(total_bb, 0))
410
0
        return;
411
412
    // Render
413
0
    RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f));
414
0
    if (label_size.x > 0.0f)
415
0
        RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);
416
0
}
417
418
void ImGui::BulletText(const char* fmt, ...)
419
0
{
420
0
    va_list args;
421
0
    va_start(args, fmt);
422
0
    BulletTextV(fmt, args);
423
0
    va_end(args);
424
0
}
425
426
// Text with a little bullet aligned to the typical tree node.
427
void ImGui::BulletTextV(const char* fmt, va_list args)
428
0
{
429
0
    ImGuiWindow* window = GetCurrentWindow();
430
0
    if (window->SkipItems)
431
0
        return;
432
433
0
    ImGuiContext& g = *GImGui;
434
0
    const ImGuiStyle& style = g.Style;
435
436
0
    const char* text_begin, *text_end;
437
0
    ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args);
438
0
    const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);
439
0
    const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y);  // Empty text doesn't add padding
440
0
    ImVec2 pos = window->DC.CursorPos;
441
0
    pos.y += window->DC.CurrLineTextBaseOffset;
442
0
    ItemSize(total_size, 0.0f);
443
0
    const ImRect bb(pos, pos + total_size);
444
0
    if (!ItemAdd(bb, 0))
445
0
        return;
446
447
    // Render
448
0
    ImU32 text_col = GetColorU32(ImGuiCol_Text);
449
0
    RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col);
450
0
    RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false);
451
0
}
452
453
//-------------------------------------------------------------------------
454
// [SECTION] Widgets: Main
455
//-------------------------------------------------------------------------
456
// - ButtonBehavior() [Internal]
457
// - Button()
458
// - SmallButton()
459
// - InvisibleButton()
460
// - ArrowButton()
461
// - CloseButton() [Internal]
462
// - CollapseButton() [Internal]
463
// - GetWindowScrollbarID() [Internal]
464
// - GetWindowScrollbarRect() [Internal]
465
// - Scrollbar() [Internal]
466
// - ScrollbarEx() [Internal]
467
// - Image()
468
// - ImageButton()
469
// - Checkbox()
470
// - CheckboxFlagsT() [Internal]
471
// - CheckboxFlags()
472
// - RadioButton()
473
// - ProgressBar()
474
// - Bullet()
475
// - Hyperlink()
476
//-------------------------------------------------------------------------
477
478
// The ButtonBehavior() function is key to many interactions and used by many/most widgets.
479
// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_),
480
// this code is a little complex.
481
// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior.
482
// See the series of events below and the corresponding state reported by dear imgui:
483
//------------------------------------------------------------------------------------------------------------------------------------------------
484
// with PressedOnClickRelease:             return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()
485
//   Frame N+0 (mouse is outside bb)        -             -                -               -                  -                    -
486
//   Frame N+1 (mouse moves inside bb)      -             true             -               -                  -                    -
487
//   Frame N+2 (mouse button is down)       -             true             true            true               -                    true
488
//   Frame N+3 (mouse button is down)       -             true             true            -                  -                    -
489
//   Frame N+4 (mouse moves outside bb)     -             -                true            -                  -                    -
490
//   Frame N+5 (mouse moves inside bb)      -             true             true            -                  -                    -
491
//   Frame N+6 (mouse button is released)   true          true             -               -                  true                 -
492
//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -
493
//   Frame N+8 (mouse moves outside bb)     -             -                -               -                  -                    -
494
//------------------------------------------------------------------------------------------------------------------------------------------------
495
// with PressedOnClick:                    return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()
496
//   Frame N+2 (mouse button is down)       true          true             true            true               -                    true
497
//   Frame N+3 (mouse button is down)       -             true             true            -                  -                    -
498
//   Frame N+6 (mouse button is released)   -             true             -               -                  true                 -
499
//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -
500
//------------------------------------------------------------------------------------------------------------------------------------------------
501
// with PressedOnRelease:                  return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()
502
//   Frame N+2 (mouse button is down)       -             true             -               -                  -                    true
503
//   Frame N+3 (mouse button is down)       -             true             -               -                  -                    -
504
//   Frame N+6 (mouse button is released)   true          true             -               -                  -                    -
505
//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -
506
//------------------------------------------------------------------------------------------------------------------------------------------------
507
// with PressedOnDoubleClick:              return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()
508
//   Frame N+0 (mouse button is down)       -             true             -               -                  -                    true
509
//   Frame N+1 (mouse button is down)       -             true             -               -                  -                    -
510
//   Frame N+2 (mouse button is released)   -             true             -               -                  -                    -
511
//   Frame N+3 (mouse button is released)   -             true             -               -                  -                    -
512
//   Frame N+4 (mouse button is down)       true          true             true            true               -                    true
513
//   Frame N+5 (mouse button is down)       -             true             true            -                  -                    -
514
//   Frame N+6 (mouse button is released)   -             true             -               -                  true                 -
515
//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -
516
//------------------------------------------------------------------------------------------------------------------------------------------------
517
// Note that some combinations are supported,
518
// - PressedOnDragDropHold can generally be associated with any flag.
519
// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported.
520
//------------------------------------------------------------------------------------------------------------------------------------------------
521
// The behavior of the return-value changes when ImGuiItemFlags_ButtonRepeat is set:
522
//                                         Repeat+                  Repeat+           Repeat+             Repeat+
523
//                                         PressedOnClickRelease    PressedOnClick    PressedOnRelease    PressedOnDoubleClick
524
//-------------------------------------------------------------------------------------------------------------------------------------------------
525
//   Frame N+0 (mouse button is down)       -                        true              -                   true
526
//   ...                                    -                        -                 -                   -
527
//   Frame N + RepeatDelay                  true                     true              -                   true
528
//   ...                                    -                        -                 -                   -
529
//   Frame N + RepeatDelay + RepeatRate*N   true                     true              -                   true
530
//-------------------------------------------------------------------------------------------------------------------------------------------------
531
532
// - FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc.
533
//   And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);'
534
//   For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading.
535
// - Since v1.91.2 (Sept 2024) we included io.ConfigDebugHighlightIdConflicts feature.
536
//   One idiom which was previously valid which will now emit a warning is when using multiple overlaid ButtonBehavior()
537
//   with same ID and different MouseButton (see #8030). You can fix it by:
538
//       (1) switching to use a single ButtonBehavior() with multiple _MouseButton flags.
539
//    or (2) surrounding those calls with PushItemFlag(ImGuiItemFlags_AllowDuplicateId, true); ... PopItemFlag()
540
bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)
541
324k
{
542
324k
    ImGuiContext& g = *GImGui;
543
324k
    ImGuiWindow* window = GetCurrentWindow();
544
545
    // Default behavior inherited from item flags
546
    // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.
547
324k
    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.ItemFlags : g.CurrentItemFlags);
548
324k
    if (flags & ImGuiButtonFlags_AllowOverlap)
549
0
        item_flags |= ImGuiItemFlags_AllowOverlap;
550
324k
    if (item_flags & ImGuiItemFlags_NoFocus)
551
0
        flags |= ImGuiButtonFlags_NoFocus | ImGuiButtonFlags_NoNavFocus;
552
553
    // Default only reacts to left mouse button
554
324k
    if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)
555
324k
        flags |= ImGuiButtonFlags_MouseButtonLeft;
556
557
    // Default behavior requires click + release inside bounding box
558
324k
    if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0)
559
80.5k
        flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_;
560
561
324k
    ImGuiWindow* backup_hovered_window = g.HoveredWindow;
562
324k
    const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window->RootWindow;
563
324k
    if (flatten_hovered_children)
564
0
        g.HoveredWindow = window;
565
566
#ifdef IMGUI_ENABLE_TEST_ENGINE
567
    // Alternate registration spot, for when caller didn't use ItemAdd()
568
    if (g.LastItemData.ID != id)
569
        IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL);
570
#endif
571
572
324k
    bool pressed = false;
573
324k
    bool hovered = ItemHoverable(bb, id, item_flags);
574
575
    // Special mode for Drag and Drop used by openables (tree nodes, tabs etc.)
576
    // where holding the button pressed for a long time while drag a payload item triggers the button.
577
324k
    if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
578
0
        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
579
0
        {
580
0
            hovered = true;
581
0
            SetHoveredID(id);
582
0
            if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER)
583
0
            {
584
0
                pressed = true;
585
0
                g.DragDropHoldJustPressedId = id;
586
0
                FocusWindow(window);
587
0
            }
588
0
        }
589
590
324k
    if (flatten_hovered_children)
591
0
        g.HoveredWindow = backup_hovered_window;
592
593
    // Mouse handling
594
324k
    const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;
595
324k
    if (hovered)
596
1.15k
    {
597
1.15k
        IM_ASSERT(id != 0); // Lazily check inside rare path.
598
599
        // Poll mouse buttons
600
        // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId.
601
        // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine.
602
1.15k
        int mouse_button_clicked = -1;
603
1.15k
        int mouse_button_released = -1;
604
4.60k
        for (int button = 0; button < 3; button++)
605
3.45k
            if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here.
606
1.15k
            {
607
1.15k
                if (IsMouseClicked(button, ImGuiInputFlags_None, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; }
608
1.15k
                if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; }
609
1.15k
            }
610
611
        // Process initial action
612
1.15k
        const bool mods_ok = !(flags & ImGuiButtonFlags_NoKeyModsAllowed) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt);
613
1.15k
        if (mods_ok)
614
1.15k
        {
615
1.15k
            if (mouse_button_clicked != -1 && g.ActiveId != id)
616
2
            {
617
2
                if (!(flags & ImGuiButtonFlags_NoSetKeyOwner))
618
0
                    SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id);
619
2
                if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere))
620
0
                {
621
0
                    SetActiveID(id, window);
622
0
                    g.ActiveIdMouseButton = mouse_button_clicked;
623
0
                    if (!(flags & ImGuiButtonFlags_NoNavFocus))
624
0
                    {
625
0
                        SetFocusID(id, window);
626
0
                        FocusWindow(window);
627
0
                    }
628
0
                    else if (!(flags & ImGuiButtonFlags_NoFocus))
629
0
                    {
630
0
                        FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
631
0
                    }
632
0
                }
633
2
                if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2))
634
1
                {
635
1
                    pressed = true;
636
1
                    if (flags & ImGuiButtonFlags_NoHoldingActiveId)
637
1
                        ClearActiveID();
638
0
                    else
639
0
                        SetActiveID(id, window); // Hold on ID
640
1
                    g.ActiveIdMouseButton = mouse_button_clicked;
641
1
                    if (!(flags & ImGuiButtonFlags_NoNavFocus))
642
1
                    {
643
1
                        SetFocusID(id, window);
644
1
                        FocusWindow(window);
645
1
                    }
646
0
                    else if (!(flags & ImGuiButtonFlags_NoFocus))
647
0
                    {
648
0
                        FocusWindow(window, ImGuiFocusRequestFlags_RestoreFocusedChild); // Still need to focus and bring to front, but try to avoid losing NavId when navigating a child
649
0
                    }
650
1
                }
651
2
            }
652
1.15k
            if (flags & ImGuiButtonFlags_PressedOnRelease)
653
405
            {
654
405
                if (mouse_button_released != -1)
655
1
                {
656
1
                    const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior
657
1
                    if (!has_repeated_at_least_once)
658
1
                        pressed = true;
659
1
                    if (!(flags & ImGuiButtonFlags_NoNavFocus))
660
1
                        SetFocusID(id, window); // FIXME: Lack of FocusWindow() call here is inconsistent with other paths. Research why.
661
1
                    ClearActiveID();
662
1
                }
663
405
            }
664
665
            // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).
666
            // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.
667
1.15k
            if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat))
668
0
                if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, ImGuiInputFlags_Repeat, test_owner_id))
669
0
                    pressed = true;
670
1.15k
        }
671
672
1.15k
        if (pressed && g.IO.ConfigNavCursorVisibleAuto)
673
2
            g.NavCursorVisible = false;
674
1.15k
    }
675
676
    // Keyboard/Gamepad navigation handling
677
    // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse.
678
324k
    if (g.NavId == id && g.NavCursorVisible && g.NavHighlightItemUnderNav)
679
0
        if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))
680
0
            hovered = true;
681
324k
    if (g.NavActivateDownId == id)
682
0
    {
683
0
        bool nav_activated_by_code = (g.NavActivateId == id);
684
0
        bool nav_activated_by_inputs = (g.NavActivatePressedId == id);
685
0
        if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat))
686
0
        {
687
            // Avoid pressing multiple keys from triggering excessive amount of repeat events
688
0
            const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);
689
0
            const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter);
690
0
            const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate);
691
0
            const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration);
692
0
            nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;
693
0
        }
694
0
        if (nav_activated_by_code || nav_activated_by_inputs)
695
0
        {
696
            // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.
697
0
            pressed = true;
698
0
            SetActiveID(id, window);
699
0
            g.ActiveIdSource = g.NavInputSource;
700
0
            if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut))
701
0
                SetFocusID(id, window);
702
0
            if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)
703
0
                g.ActiveIdFromShortcut = true;
704
0
        }
705
0
    }
706
707
    // Process while held
708
324k
    bool held = false;
709
324k
    if (g.ActiveId == id)
710
0
    {
711
0
        if (g.ActiveIdSource == ImGuiInputSource_Mouse)
712
0
        {
713
0
            if (g.ActiveIdIsJustActivated)
714
0
                g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;
715
716
0
            const int mouse_button = g.ActiveIdMouseButton;
717
0
            if (mouse_button == -1)
718
0
            {
719
                // Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304).
720
0
                ClearActiveID();
721
0
            }
722
0
            else if (IsMouseDown(mouse_button, test_owner_id))
723
0
            {
724
0
                held = true;
725
0
            }
726
0
            else
727
0
            {
728
0
                bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0;
729
0
                bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0;
730
0
                if ((release_in || release_anywhere) && !g.DragDropActive)
731
0
                {
732
                    // Report as pressed when releasing the mouse (this is the most common path)
733
0
                    bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2;
734
0
                    bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release>
735
0
                    bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);
736
0
                    if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)
737
0
                        pressed = true;
738
0
                }
739
0
                ClearActiveID();
740
0
            }
741
0
            if (!(flags & ImGuiButtonFlags_NoNavFocus) && g.IO.ConfigNavCursorVisibleAuto)
742
0
                g.NavCursorVisible = false;
743
0
        }
744
0
        else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
745
0
        {
746
            // When activated using Nav, we hold on the ActiveID until activation button is released
747
0
            if (g.NavActivateDownId == id)
748
0
                held = true; // hovered == true not true as we are already likely hovered on direct activation.
749
0
            else
750
0
                ClearActiveID();
751
0
        }
752
0
        if (pressed)
753
0
            g.ActiveIdHasBeenPressedBefore = true;
754
0
    }
755
756
    // Activation highlight (this may be a remote activation)
757
324k
    if (g.NavHighlightActivatedId == id)
758
0
        hovered = true;
759
760
324k
    if (out_hovered) *out_hovered = hovered;
761
324k
    if (out_held) *out_held = held;
762
763
324k
    return pressed;
764
324k
}
765
766
bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
767
0
{
768
0
    ImGuiWindow* window = GetCurrentWindow();
769
0
    if (window->SkipItems)
770
0
        return false;
771
772
0
    ImGuiContext& g = *GImGui;
773
0
    const ImGuiStyle& style = g.Style;
774
0
    const ImGuiID id = window->GetID(label);
775
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
776
777
0
    ImVec2 pos = window->DC.CursorPos;
778
0
    if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
779
0
        pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;
780
0
    ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);
781
782
0
    const ImRect bb(pos, pos + size);
783
0
    ItemSize(size, style.FramePadding.y);
784
0
    if (!ItemAdd(bb, id))
785
0
        return false;
786
787
0
    bool hovered, held;
788
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
789
790
    // Render
791
0
    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
792
0
    RenderNavCursor(bb, id);
793
0
    RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
794
795
0
    if (g.LogEnabled)
796
0
        LogSetNextTextDecoration("[", "]");
797
0
    RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
798
799
    // Automatically close popups
800
    //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
801
    //    CloseCurrentPopup();
802
803
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
804
0
    return pressed;
805
0
}
806
807
bool ImGui::Button(const char* label, const ImVec2& size_arg)
808
0
{
809
0
    return ButtonEx(label, size_arg, ImGuiButtonFlags_None);
810
0
}
811
812
// Small buttons fits within text without additional vertical spacing.
813
bool ImGui::SmallButton(const char* label)
814
0
{
815
0
    ImGuiContext& g = *GImGui;
816
0
    float backup_padding_y = g.Style.FramePadding.y;
817
0
    g.Style.FramePadding.y = 0.0f;
818
0
    bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);
819
0
    g.Style.FramePadding.y = backup_padding_y;
820
0
    return pressed;
821
0
}
822
823
// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.
824
// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)
825
bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags)
826
0
{
827
0
    ImGuiContext& g = *GImGui;
828
0
    ImGuiWindow* window = GetCurrentWindow();
829
0
    if (window->SkipItems)
830
0
        return false;
831
832
    // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size.
833
0
    IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f);
834
835
0
    const ImGuiID id = window->GetID(str_id);
836
0
    ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);
837
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
838
0
    ItemSize(size);
839
0
    if (!ItemAdd(bb, id, NULL, (flags & ImGuiButtonFlags_EnableNav) ? ImGuiItemFlags_None : ImGuiItemFlags_NoNav))
840
0
        return false;
841
842
0
    bool hovered, held;
843
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
844
0
    RenderNavCursor(bb, id);
845
846
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
847
0
    return pressed;
848
0
}
849
850
bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)
851
0
{
852
0
    ImGuiContext& g = *GImGui;
853
0
    ImGuiWindow* window = GetCurrentWindow();
854
0
    if (window->SkipItems)
855
0
        return false;
856
857
0
    const ImGuiID id = window->GetID(str_id);
858
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
859
0
    const float default_size = GetFrameHeight();
860
0
    ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f);
861
0
    if (!ItemAdd(bb, id))
862
0
        return false;
863
864
0
    bool hovered, held;
865
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
866
867
    // Render
868
0
    const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
869
0
    const ImU32 text_col = GetColorU32(ImGuiCol_Text);
870
0
    RenderNavCursor(bb, id);
871
0
    RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);
872
0
    RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir);
873
874
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
875
0
    return pressed;
876
0
}
877
878
bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)
879
0
{
880
0
    float sz = GetFrameHeight();
881
0
    return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None);
882
0
}
883
884
// Button to close a window
885
bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
886
0
{
887
0
    ImGuiContext& g = *GImGui;
888
0
    ImGuiWindow* window = g.CurrentWindow;
889
890
    // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825)
891
    // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible?
892
0
    const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));
893
0
    ImRect bb_interact = bb;
894
0
    const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea();
895
0
    if (area_to_visible_ratio < 1.5f)
896
0
        bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f));
897
898
    // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window.
899
    // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer).
900
0
    bool is_clipped = !ItemAdd(bb_interact, id);
901
902
0
    bool hovered, held;
903
0
    bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held);
904
0
    if (is_clipped)
905
0
        return pressed;
906
907
    // Render
908
0
    ImU32 bg_col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered);
909
0
    if (hovered)
910
0
        window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
911
0
    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
912
0
    const ImU32 cross_col = GetColorU32(ImGuiCol_Text);
913
0
    const ImVec2 cross_center = bb.GetCenter() - ImVec2(0.5f, 0.5f);
914
0
    const float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;
915
0
    const float cross_thickness = 1.0f; // FIXME-DPI
916
0
    window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, +cross_extent), cross_center + ImVec2(-cross_extent, -cross_extent), cross_col, cross_thickness);
917
0
    window->DrawList->AddLine(cross_center + ImVec2(+cross_extent, -cross_extent), cross_center + ImVec2(-cross_extent, +cross_extent), cross_col, cross_thickness);
918
919
0
    return pressed;
920
0
}
921
922
bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)
923
80.5k
{
924
80.5k
    ImGuiContext& g = *GImGui;
925
80.5k
    ImGuiWindow* window = g.CurrentWindow;
926
927
80.5k
    ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));
928
80.5k
    bool is_clipped = !ItemAdd(bb, id);
929
80.5k
    bool hovered, held;
930
80.5k
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);
931
80.5k
    if (is_clipped)
932
0
        return pressed;
933
934
    // Render
935
80.5k
    ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
936
80.5k
    ImU32 text_col = GetColorU32(ImGuiCol_Text);
937
80.5k
    if (hovered || held)
938
0
        window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col);
939
80.5k
    RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact);
940
80.5k
    RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);
941
942
    // Switch to moving the window after mouse is moved beyond the initial drag threshold
943
80.5k
    if (IsItemActive() && IsMouseDragging(0))
944
0
        StartMouseMovingWindow(window);
945
946
80.5k
    return pressed;
947
80.5k
}
948
949
ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)
950
0
{
951
0
    return window->GetID(axis == ImGuiAxis_X ? "#SCROLLX" : "#SCROLLY");
952
0
}
953
954
// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.
955
ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)
956
0
{
957
0
    ImGuiContext& g = *GImGui;
958
0
    const ImRect outer_rect = window->Rect();
959
0
    const ImRect inner_rect = window->InnerRect;
960
0
    const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)
961
0
    IM_ASSERT(scrollbar_size >= 0.0f);
962
0
    const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f);
963
0
    const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f;
964
0
    if (axis == ImGuiAxis_X)
965
0
        return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size);
966
0
    else
967
0
        return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y + border_top, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size);
968
0
}
969
970
void ImGui::Scrollbar(ImGuiAxis axis)
971
0
{
972
0
    ImGuiContext& g = *GImGui;
973
0
    ImGuiWindow* window = g.CurrentWindow;
974
0
    const ImGuiID id = GetWindowScrollbarID(window, axis);
975
976
    // Calculate scrollbar bounding box
977
0
    ImRect bb = GetWindowScrollbarRect(window, axis);
978
0
    ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone;
979
0
    if (axis == ImGuiAxis_X)
980
0
    {
981
0
        rounding_corners |= ImDrawFlags_RoundCornersBottomLeft;
982
0
        if (!window->ScrollbarY)
983
0
            rounding_corners |= ImDrawFlags_RoundCornersBottomRight;
984
0
    }
985
0
    else
986
0
    {
987
0
        if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar))
988
0
            rounding_corners |= ImDrawFlags_RoundCornersTopRight;
989
0
        if (!window->ScrollbarX)
990
0
            rounding_corners |= ImDrawFlags_RoundCornersBottomRight;
991
0
    }
992
0
    float size_visible = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];
993
0
    float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;
994
0
    ImS64 scroll = (ImS64)window->Scroll[axis];
995
0
    ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_visible, (ImS64)size_contents, rounding_corners);
996
0
    window->Scroll[axis] = (float)scroll;
997
0
}
998
999
// Vertical/Horizontal scrollbar
1000
// The entire piece of code below is rather confusing because:
1001
// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)
1002
// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar
1003
// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.
1004
// Still, the code should probably be made simpler..
1005
bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_visible_v, ImS64 size_contents_v, ImDrawFlags draw_rounding_flags)
1006
0
{
1007
0
    ImGuiContext& g = *GImGui;
1008
0
    ImGuiWindow* window = g.CurrentWindow;
1009
0
    if (window->SkipItems)
1010
0
        return false;
1011
1012
0
    const float bb_frame_width = bb_frame.GetWidth();
1013
0
    const float bb_frame_height = bb_frame.GetHeight();
1014
0
    if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)
1015
0
        return false;
1016
1017
    // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)
1018
0
    float alpha = 1.0f;
1019
0
    if ((axis == ImGuiAxis_Y) && bb_frame_height < bb_frame_width)
1020
0
        alpha = ImSaturate(bb_frame_height / ImMax(bb_frame_width * 2.0f, 1.0f));
1021
0
    if (alpha <= 0.0f)
1022
0
        return false;
1023
1024
0
    const ImGuiStyle& style = g.Style;
1025
0
    const bool allow_interaction = (alpha >= 1.0f);
1026
1027
0
    ImRect bb = bb_frame;
1028
0
    float padding = IM_TRUNC(ImMin(style.ScrollbarPadding, ImMin(bb_frame_width, bb_frame_height) * 0.5f));
1029
0
    bb.Expand(-padding);
1030
1031
    // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
1032
0
    const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
1033
1034
    // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)
1035
    // But we maintain a minimum size in pixel to allow for the user to still aim inside.
1036
0
    IM_ASSERT(ImMax(size_contents_v, size_visible_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.
1037
0
    const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_visible_v), (ImS64)1);
1038
0
    const float grab_h_minsize = ImMin(bb.GetSize()[axis], style.GrabMinSize);
1039
0
    const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_visible_v / (float)win_size_v), grab_h_minsize, scrollbar_size_v);
1040
0
    const float grab_h_norm = grab_h_pixels / scrollbar_size_v;
1041
1042
    // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().
1043
0
    bool held = false;
1044
0
    bool hovered = false;
1045
0
    ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav);
1046
0
    ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);
1047
1048
0
    const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_visible_v);
1049
0
    float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);
1050
0
    float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space
1051
0
    if (held && allow_interaction && grab_h_norm < 1.0f)
1052
0
    {
1053
0
        const float scrollbar_pos_v = bb.Min[axis];
1054
0
        const float mouse_pos_v = g.IO.MousePos[axis];
1055
1056
        // Click position in scrollbar normalized space (0.0f->1.0f)
1057
0
        const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);
1058
1059
0
        const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0;
1060
0
        if (g.ActiveIdIsJustActivated)
1061
0
        {
1062
            // On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab
1063
0
            const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0);
1064
0
            g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir;
1065
0
            g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f;
1066
0
        }
1067
1068
        // Apply scroll (p_scroll_v will generally point on one member of window->Scroll)
1069
        // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position
1070
0
        if (g.ScrollbarSeekMode == 0)
1071
0
        {
1072
            // Absolute seeking
1073
0
            const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));
1074
0
            *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max);
1075
0
        }
1076
0
        else
1077
0
        {
1078
            // Page by page
1079
0
            if (IsMouseClicked(ImGuiMouseButton_Left, ImGuiInputFlags_Repeat) && held_dir == g.ScrollbarSeekMode)
1080
0
            {
1081
0
                float page_dir = (g.ScrollbarSeekMode > 0.0f) ? +1.0f : -1.0f;
1082
0
                *p_scroll_v = ImClamp(*p_scroll_v + (ImS64)(page_dir * size_visible_v), (ImS64)0, scroll_max);
1083
0
            }
1084
0
        }
1085
1086
        // Update values for rendering
1087
0
        scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);
1088
0
        grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;
1089
1090
        // Update distance to grab now that we have seek'ed and saturated
1091
        //if (seek_absolute)
1092
        //    g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;
1093
0
    }
1094
1095
    // Render
1096
0
    const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);
1097
0
    const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);
1098
0
    window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, draw_rounding_flags);
1099
0
    ImRect grab_rect;
1100
0
    if (axis == ImGuiAxis_X)
1101
0
        grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);
1102
0
    else
1103
0
        grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels);
1104
0
    window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);
1105
1106
0
    return held;
1107
0
}
1108
1109
// - Read about ImTextureID/ImTextureRef here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
1110
// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above.
1111
void ImGui::ImageWithBg(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
1112
0
{
1113
0
    ImGuiContext& g = *GImGui;
1114
0
    ImGuiWindow* window = GetCurrentWindow();
1115
0
    if (window->SkipItems)
1116
0
        return;
1117
1118
0
    const ImVec2 padding(g.Style.ImageBorderSize, g.Style.ImageBorderSize);
1119
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
1120
0
    ItemSize(bb);
1121
0
    if (!ItemAdd(bb, 0))
1122
0
        return;
1123
1124
    // Render
1125
0
    if (g.Style.ImageBorderSize > 0.0f)
1126
0
        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, ImDrawFlags_None, g.Style.ImageBorderSize);
1127
0
    if (bg_col.w > 0.0f)
1128
0
        window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
1129
0
    window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
1130
0
}
1131
1132
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1)
1133
0
{
1134
0
    ImageWithBg(tex_ref, image_size, uv0, uv1);
1135
0
}
1136
1137
// 1.91.9 (February 2025) removed 'tint_col' and 'border_col' parameters, made border size not depend on color value. (#8131, #8238)
1138
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1139
void ImGui::Image(ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
1140
0
{
1141
0
    ImGuiContext& g = *GImGui;
1142
0
    PushStyleVar(ImGuiStyleVar_ImageBorderSize, (border_col.w > 0.0f) ? ImMax(1.0f, g.Style.ImageBorderSize) : 0.0f); // Preserve legacy behavior where border is always visible when border_col's Alpha is >0.0f
1143
0
    PushStyleColor(ImGuiCol_Border, border_col);
1144
0
    ImageWithBg(tex_ref, image_size, uv0, uv1, ImVec4(0, 0, 0, 0), tint_col);
1145
0
    PopStyleColor();
1146
0
    PopStyleVar();
1147
0
}
1148
#endif
1149
1150
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
1151
0
{
1152
0
    ImGuiContext& g = *GImGui;
1153
0
    ImGuiWindow* window = GetCurrentWindow();
1154
0
    if (window->SkipItems)
1155
0
        return false;
1156
1157
0
    const ImVec2 padding = g.Style.FramePadding;
1158
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
1159
0
    ItemSize(bb);
1160
0
    if (!ItemAdd(bb, id))
1161
0
        return false;
1162
1163
0
    bool hovered, held;
1164
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);
1165
1166
    // Render
1167
0
    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
1168
0
    RenderNavCursor(bb, id);
1169
0
    RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
1170
0
    if (bg_col.w > 0.0f)
1171
0
        window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));
1172
0
    window->DrawList->AddImage(tex_ref, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));
1173
1174
0
    return pressed;
1175
0
}
1176
1177
// - ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
1178
// - ImageButton() draws a background based on regular Button() color + optionally an inner background if specified. (#8165) // FIXME: Maybe that's not the best design?
1179
bool ImGui::ImageButton(const char* str_id, ImTextureRef tex_ref, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
1180
0
{
1181
0
    ImGuiContext& g = *GImGui;
1182
0
    ImGuiWindow* window = g.CurrentWindow;
1183
0
    if (window->SkipItems)
1184
0
        return false;
1185
1186
0
    return ImageButtonEx(window->GetID(str_id), tex_ref, image_size, uv0, uv1, bg_col, tint_col);
1187
0
}
1188
1189
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1190
// Legacy API obsoleted in 1.89. Two differences with new ImageButton()
1191
// - old ImageButton() used ImTextureID as item id (created issue with multiple buttons with same image, transient texture id values, opaque computation of ID)
1192
// - new ImageButton() requires an explicit 'const char* str_id'
1193
// - old ImageButton() had frame_padding' override argument.
1194
// - new ImageButton() always use style.FramePadding.
1195
/*
1196
bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)
1197
{
1198
    // Default to using texture ID as ID. User can still push string/integer prefixes.
1199
    PushID((ImTextureID)(intptr_t)user_texture_id);
1200
    if (frame_padding >= 0)
1201
        PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding));
1202
    bool ret = ImageButton("", user_texture_id, size, uv0, uv1, bg_col, tint_col);
1203
    if (frame_padding >= 0)
1204
        PopStyleVar();
1205
    PopID();
1206
    return ret;
1207
}
1208
*/
1209
#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1210
1211
bool ImGui::Checkbox(const char* label, bool* v)
1212
0
{
1213
0
    ImGuiWindow* window = GetCurrentWindow();
1214
0
    if (window->SkipItems)
1215
0
        return false;
1216
1217
0
    ImGuiContext& g = *GImGui;
1218
0
    const ImGuiStyle& style = g.Style;
1219
0
    const ImGuiID id = window->GetID(label);
1220
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
1221
1222
0
    const float square_sz = GetFrameHeight();
1223
0
    const ImVec2 pos = window->DC.CursorPos;
1224
0
    const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
1225
0
    ItemSize(total_bb, style.FramePadding.y);
1226
0
    const bool is_visible = ItemAdd(total_bb, id);
1227
0
    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
1228
0
    if (!is_visible)
1229
0
        if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(total_bb)) // Extra layer of "no logic clip" for box-select support
1230
0
        {
1231
0
            IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
1232
0
            return false;
1233
0
        }
1234
1235
    // Range-Selection/Multi-selection support (header)
1236
0
    bool checked = *v;
1237
0
    if (is_multi_select)
1238
0
        MultiSelectItemHeader(id, &checked, NULL);
1239
1240
0
    bool hovered, held;
1241
0
    bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
1242
1243
    // Range-Selection/Multi-selection support (footer)
1244
0
    if (is_multi_select)
1245
0
        MultiSelectItemFooter(id, &checked, &pressed);
1246
0
    else if (pressed)
1247
0
        checked = !checked;
1248
1249
0
    if (*v != checked)
1250
0
    {
1251
0
        *v = checked;
1252
0
        pressed = true; // return value
1253
0
        MarkItemEdited(id);
1254
0
    }
1255
1256
0
    const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
1257
0
    const bool mixed_value = (g.LastItemData.ItemFlags & ImGuiItemFlags_MixedValue) != 0;
1258
0
    if (is_visible)
1259
0
    {
1260
0
        RenderNavCursor(total_bb, id);
1261
0
        RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
1262
0
        ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);
1263
0
        if (mixed_value)
1264
0
        {
1265
            // Undocumented tristate/mixed/indeterminate checkbox (#2644)
1266
            // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox)
1267
0
            ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)));
1268
0
            window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);
1269
0
        }
1270
0
        else if (*v)
1271
0
        {
1272
0
            const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
1273
0
            RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);
1274
0
        }
1275
0
    }
1276
0
    const ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
1277
0
    if (g.LogEnabled)
1278
0
        LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]");
1279
0
    if (is_visible && label_size.x > 0.0f)
1280
0
        RenderText(label_pos, label);
1281
1282
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
1283
0
    return pressed;
1284
0
}
1285
1286
template<typename T>
1287
bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value)
1288
0
{
1289
0
    bool all_on = (*flags & flags_value) == flags_value;
1290
0
    bool any_on = (*flags & flags_value) != 0;
1291
0
    bool pressed;
1292
0
    if (!all_on && any_on)
1293
0
    {
1294
0
        ImGuiContext& g = *GImGui;
1295
0
        g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue;
1296
0
        pressed = Checkbox(label, &all_on);
1297
0
    }
1298
0
    else
1299
0
    {
1300
0
        pressed = Checkbox(label, &all_on);
1301
1302
0
    }
1303
0
    if (pressed)
1304
0
    {
1305
0
        if (all_on)
1306
0
            *flags |= flags_value;
1307
0
        else
1308
0
            *flags &= ~flags_value;
1309
0
    }
1310
0
    return pressed;
1311
0
}
Unexecuted instantiation: _ZN5ImGui14CheckboxFlagsTIiEEbPKcPT_S3_
Unexecuted instantiation: _ZN5ImGui14CheckboxFlagsTIjEEbPKcPT_S3_
Unexecuted instantiation: _ZN5ImGui14CheckboxFlagsTIxEEbPKcPT_S3_
Unexecuted instantiation: _ZN5ImGui14CheckboxFlagsTIyEEbPKcPT_S3_
1312
1313
bool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value)
1314
0
{
1315
0
    return CheckboxFlagsT(label, flags, flags_value);
1316
0
}
1317
1318
bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)
1319
0
{
1320
0
    return CheckboxFlagsT(label, flags, flags_value);
1321
0
}
1322
1323
bool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value)
1324
0
{
1325
0
    return CheckboxFlagsT(label, flags, flags_value);
1326
0
}
1327
1328
bool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value)
1329
0
{
1330
0
    return CheckboxFlagsT(label, flags, flags_value);
1331
0
}
1332
1333
bool ImGui::RadioButton(const char* label, bool active)
1334
0
{
1335
0
    ImGuiWindow* window = GetCurrentWindow();
1336
0
    if (window->SkipItems)
1337
0
        return false;
1338
1339
0
    ImGuiContext& g = *GImGui;
1340
0
    const ImGuiStyle& style = g.Style;
1341
0
    const ImGuiID id = window->GetID(label);
1342
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
1343
1344
0
    const float square_sz = GetFrameHeight();
1345
0
    const ImVec2 pos = window->DC.CursorPos;
1346
0
    const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
1347
0
    const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
1348
0
    ItemSize(total_bb, style.FramePadding.y);
1349
0
    if (!ItemAdd(total_bb, id))
1350
0
        return false;
1351
1352
0
    ImVec2 center = check_bb.GetCenter();
1353
0
    center.x = IM_ROUND(center.x);
1354
0
    center.y = IM_ROUND(center.y);
1355
0
    const float radius = (square_sz - 1.0f) * 0.5f;
1356
1357
0
    bool hovered, held;
1358
0
    bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);
1359
0
    if (pressed)
1360
0
        MarkItemEdited(id);
1361
1362
0
    RenderNavCursor(total_bb, id);
1363
0
    const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius);
1364
0
    window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);
1365
0
    if (active)
1366
0
    {
1367
0
        const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
1368
0
        window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));
1369
0
    }
1370
1371
0
    if (style.FrameBorderSize > 0.0f)
1372
0
    {
1373
0
        window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize);
1374
0
        window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize);
1375
0
    }
1376
1377
0
    ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
1378
0
    if (g.LogEnabled)
1379
0
        LogRenderedText(&label_pos, active ? "(x)" : "( )");
1380
0
    if (label_size.x > 0.0f)
1381
0
        RenderText(label_pos, label);
1382
1383
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
1384
0
    return pressed;
1385
0
}
1386
1387
// FIXME: This would work nicely if it was a public template, e.g. 'template<T> RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it..
1388
bool ImGui::RadioButton(const char* label, int* v, int v_button)
1389
0
{
1390
0
    const bool pressed = RadioButton(label, *v == v_button);
1391
0
    if (pressed)
1392
0
        *v = v_button;
1393
0
    return pressed;
1394
0
}
1395
1396
// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size
1397
void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)
1398
0
{
1399
0
    ImGuiWindow* window = GetCurrentWindow();
1400
0
    if (window->SkipItems)
1401
0
        return;
1402
1403
0
    ImGuiContext& g = *GImGui;
1404
0
    const ImGuiStyle& style = g.Style;
1405
1406
0
    ImVec2 pos = window->DC.CursorPos;
1407
0
    ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f);
1408
0
    ImRect bb(pos, pos + size);
1409
0
    ItemSize(size, style.FramePadding.y);
1410
0
    if (!ItemAdd(bb, 0))
1411
0
        return;
1412
1413
    // Fraction < 0.0f will display an indeterminate progress bar animation
1414
    // The value must be animated along with time, so e.g. passing '-1.0f * ImGui::GetTime()' as fraction works.
1415
0
    const bool is_indeterminate = (fraction < 0.0f);
1416
0
    if (!is_indeterminate)
1417
0
        fraction = ImSaturate(fraction);
1418
1419
    // Out of courtesy we accept a NaN fraction without crashing
1420
0
    float fill_n0 = 0.0f;
1421
0
    float fill_n1 = (fraction == fraction) ? fraction : 0.0f;
1422
1423
0
    if (is_indeterminate)
1424
0
    {
1425
0
        const float fill_width_n = 0.2f;
1426
0
        fill_n0 = ImFmod(-fraction, 1.0f) * (1.0f + fill_width_n) - fill_width_n;
1427
0
        fill_n1 = ImSaturate(fill_n0 + fill_width_n);
1428
0
        fill_n0 = ImSaturate(fill_n0);
1429
0
    }
1430
1431
    // Render
1432
0
    RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
1433
0
    bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));
1434
0
    RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), fill_n0, fill_n1, style.FrameRounding);
1435
1436
    // Default displaying the fraction as percentage string, but user can override it
1437
    // Don't display text for indeterminate bars by default
1438
0
    char overlay_buf[32];
1439
0
    if (!is_indeterminate || overlay != NULL)
1440
0
    {
1441
0
        if (!overlay)
1442
0
        {
1443
0
            ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction * 100 + 0.01f);
1444
0
            overlay = overlay_buf;
1445
0
        }
1446
1447
0
        ImVec2 overlay_size = CalcTextSize(overlay, NULL);
1448
0
        if (overlay_size.x > 0.0f)
1449
0
        {
1450
0
            float text_x = is_indeterminate ? (bb.Min.x + bb.Max.x - overlay_size.x) * 0.5f : ImLerp(bb.Min.x, bb.Max.x, fill_n1) + style.ItemSpacing.x;
1451
0
            RenderTextClipped(ImVec2(ImClamp(text_x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb);
1452
0
        }
1453
0
    }
1454
0
}
1455
1456
void ImGui::Bullet()
1457
0
{
1458
0
    ImGuiWindow* window = GetCurrentWindow();
1459
0
    if (window->SkipItems)
1460
0
        return;
1461
1462
0
    ImGuiContext& g = *GImGui;
1463
0
    const ImGuiStyle& style = g.Style;
1464
0
    const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize);
1465
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));
1466
0
    ItemSize(bb);
1467
0
    if (!ItemAdd(bb, 0))
1468
0
    {
1469
0
        SameLine(0, style.FramePadding.x * 2);
1470
0
        return;
1471
0
    }
1472
1473
    // Render and stay on same line
1474
0
    ImU32 text_col = GetColorU32(ImGuiCol_Text);
1475
0
    RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col);
1476
0
    SameLine(0, style.FramePadding.x * 2.0f);
1477
0
}
1478
1479
// This is provided as a convenience for being an often requested feature.
1480
// FIXME-STYLE: we delayed adding as there is a larger plan to revamp the styling system.
1481
// Because of this we currently don't provide many styling options for this widget
1482
// (e.g. hovered/active colors are automatically inferred from a single color).
1483
bool ImGui::TextLink(const char* label)
1484
0
{
1485
0
    ImGuiWindow* window = GetCurrentWindow();
1486
0
    if (window->SkipItems)
1487
0
        return false;
1488
1489
0
    ImGuiContext& g = *GImGui;
1490
0
    const ImGuiID id = window->GetID(label);
1491
0
    const char* label_end = FindRenderedTextEnd(label);
1492
1493
0
    ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
1494
0
    ImVec2 size = CalcTextSize(label, label_end, true);
1495
0
    ImRect bb(pos, pos + size);
1496
0
    ItemSize(size, 0.0f);
1497
0
    if (!ItemAdd(bb, id))
1498
0
        return false;
1499
1500
0
    bool hovered, held;
1501
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held);
1502
0
    RenderNavCursor(bb, id);
1503
1504
0
    if (hovered)
1505
0
        SetMouseCursor(ImGuiMouseCursor_Hand);
1506
1507
0
    ImVec4 text_colf = g.Style.Colors[ImGuiCol_TextLink];
1508
0
    ImVec4 line_colf = text_colf;
1509
0
    {
1510
        // FIXME-STYLE: Read comments above. This widget is NOT written in the same style as some earlier widgets,
1511
        // as we are currently experimenting/planning a different styling system.
1512
0
        float h, s, v;
1513
0
        ColorConvertRGBtoHSV(text_colf.x, text_colf.y, text_colf.z, h, s, v);
1514
0
        if (held || hovered)
1515
0
        {
1516
0
            v = ImSaturate(v + (held ? 0.4f : 0.3f));
1517
0
            h = ImFmod(h + 0.02f, 1.0f);
1518
0
        }
1519
0
        ColorConvertHSVtoRGB(h, s, v, text_colf.x, text_colf.y, text_colf.z);
1520
0
        v = ImSaturate(v - 0.20f);
1521
0
        ColorConvertHSVtoRGB(h, s, v, line_colf.x, line_colf.y, line_colf.z);
1522
0
    }
1523
1524
0
    float line_y = bb.Max.y + ImFloor(g.FontBaked->Descent * g.FontBakedScale * 0.20f);
1525
0
    window->DrawList->AddLine(ImVec2(bb.Min.x, line_y), ImVec2(bb.Max.x, line_y), GetColorU32(line_colf)); // FIXME-TEXT: Underline mode // FIXME-DPI
1526
1527
0
    PushStyleColor(ImGuiCol_Text, GetColorU32(text_colf));
1528
0
    RenderText(bb.Min, label, label_end);
1529
0
    PopStyleColor();
1530
1531
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
1532
0
    return pressed;
1533
0
}
1534
1535
bool ImGui::TextLinkOpenURL(const char* label, const char* url)
1536
0
{
1537
0
    ImGuiContext& g = *GImGui;
1538
0
    if (url == NULL)
1539
0
        url = label;
1540
0
    bool pressed = TextLink(label);
1541
0
    if (pressed && g.PlatformIO.Platform_OpenInShellFn != NULL)
1542
0
        g.PlatformIO.Platform_OpenInShellFn(&g, url);
1543
0
    SetItemTooltip(LocalizeGetMsg(ImGuiLocKey_OpenLink_s), url); // It is more reassuring for user to _always_ display URL when we same as label
1544
0
    if (BeginPopupContextItem())
1545
0
    {
1546
0
        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_CopyLink)))
1547
0
            SetClipboardText(url);
1548
0
        EndPopup();
1549
0
    }
1550
0
    return pressed;
1551
0
}
1552
1553
//-------------------------------------------------------------------------
1554
// [SECTION] Widgets: Low-level Layout helpers
1555
//-------------------------------------------------------------------------
1556
// - Spacing()
1557
// - Dummy()
1558
// - NewLine()
1559
// - AlignTextToFramePadding()
1560
// - SeparatorEx() [Internal]
1561
// - Separator()
1562
// - SplitterBehavior() [Internal]
1563
// - ShrinkWidths() [Internal]
1564
//-------------------------------------------------------------------------
1565
1566
void ImGui::Spacing()
1567
0
{
1568
0
    ImGuiWindow* window = GetCurrentWindow();
1569
0
    if (window->SkipItems)
1570
0
        return;
1571
0
    ItemSize(ImVec2(0, 0));
1572
0
}
1573
1574
void ImGui::Dummy(const ImVec2& size)
1575
0
{
1576
0
    ImGuiWindow* window = GetCurrentWindow();
1577
0
    if (window->SkipItems)
1578
0
        return;
1579
1580
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
1581
0
    ItemSize(size);
1582
0
    ItemAdd(bb, 0);
1583
0
}
1584
1585
void ImGui::NewLine()
1586
0
{
1587
0
    ImGuiWindow* window = GetCurrentWindow();
1588
0
    if (window->SkipItems)
1589
0
        return;
1590
1591
0
    ImGuiContext& g = *GImGui;
1592
0
    const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;
1593
0
    window->DC.LayoutType = ImGuiLayoutType_Vertical;
1594
0
    window->DC.IsSameLine = false;
1595
0
    if (window->DC.CurrLineSize.y > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.
1596
0
        ItemSize(ImVec2(0, 0));
1597
0
    else
1598
0
        ItemSize(ImVec2(0.0f, g.FontSize));
1599
0
    window->DC.LayoutType = backup_layout_type;
1600
0
}
1601
1602
void ImGui::AlignTextToFramePadding()
1603
80.5k
{
1604
80.5k
    ImGuiWindow* window = GetCurrentWindow();
1605
80.5k
    if (window->SkipItems)
1606
0
        return;
1607
1608
80.5k
    ImGuiContext& g = *GImGui;
1609
80.5k
    window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2);
1610
80.5k
    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y);
1611
80.5k
}
1612
1613
// Horizontal/vertical separating line
1614
// FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues.
1615
// Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are.
1616
void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness)
1617
0
{
1618
0
    ImGuiWindow* window = GetCurrentWindow();
1619
0
    if (window->SkipItems)
1620
0
        return;
1621
1622
0
    ImGuiContext& g = *GImGui;
1623
0
    IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)));   // Check that only 1 option is selected
1624
0
    IM_ASSERT(thickness > 0.0f);
1625
1626
0
    if (flags & ImGuiSeparatorFlags_Vertical)
1627
0
    {
1628
        // Vertical separator, for menu bars (use current line height).
1629
0
        float y1 = window->DC.CursorPos.y;
1630
0
        float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y;
1631
0
        const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2));
1632
0
        ItemSize(ImVec2(thickness, 0.0f));
1633
0
        if (!ItemAdd(bb, 0))
1634
0
            return;
1635
1636
        // Draw
1637
0
        window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));
1638
0
        if (g.LogEnabled)
1639
0
            LogText(" |");
1640
0
    }
1641
0
    else if (flags & ImGuiSeparatorFlags_Horizontal)
1642
0
    {
1643
        // Horizontal Separator
1644
0
        float x1 = window->DC.CursorPos.x;
1645
0
        float x2 = window->WorkRect.Max.x;
1646
1647
        // Preserve legacy behavior inside Columns()
1648
        // Before Tables API happened, we relied on Separator() to span all columns of a Columns() set.
1649
        // We currently don't need to provide the same feature for tables because tables naturally have border features.
1650
0
        ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL;
1651
0
        if (columns)
1652
0
        {
1653
0
            x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03
1654
0
            x2 = window->Pos.x + window->Size.x;
1655
0
            PushColumnsBackground();
1656
0
        }
1657
1658
        // We don't provide our width to the layout so that it doesn't get feed back into AutoFit
1659
        // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell)
1660
0
        const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.
1661
0
        const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness));
1662
0
        ItemSize(ImVec2(0.0f, thickness_for_layout));
1663
1664
0
        if (ItemAdd(bb, 0))
1665
0
        {
1666
            // Draw
1667
0
            window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));
1668
0
            if (g.LogEnabled)
1669
0
                LogRenderedText(&bb.Min, "--------------------------------\n");
1670
1671
0
        }
1672
0
        if (columns)
1673
0
        {
1674
0
            PopColumnsBackground();
1675
0
            columns->LineMinY = window->DC.CursorPos.y;
1676
0
        }
1677
0
    }
1678
0
}
1679
1680
void ImGui::Separator()
1681
0
{
1682
0
    ImGuiContext& g = *GImGui;
1683
0
    ImGuiWindow* window = g.CurrentWindow;
1684
0
    if (window->SkipItems)
1685
0
        return;
1686
1687
    // Those flags should eventually be configurable by the user
1688
    // FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f.
1689
0
    ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
1690
1691
    // Only applies to legacy Columns() api as they relied on Separator() a lot.
1692
0
    if (window->DC.CurrentColumns)
1693
0
        flags |= ImGuiSeparatorFlags_SpanAllColumns;
1694
1695
0
    SeparatorEx(flags, 1.0f);
1696
0
}
1697
1698
void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w)
1699
0
{
1700
0
    ImGuiContext& g = *GImGui;
1701
0
    ImGuiWindow* window = g.CurrentWindow;
1702
0
    ImGuiStyle& style = g.Style;
1703
1704
0
    const ImVec2 label_size = CalcTextSize(label, label_end, false);
1705
0
    const ImVec2 pos = window->DC.CursorPos;
1706
0
    const ImVec2 padding = style.SeparatorTextPadding;
1707
1708
0
    const float separator_thickness = style.SeparatorTextBorderSize;
1709
0
    const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));
1710
0
    const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));
1711
0
    const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f));
1712
0
    ItemSize(min_size, text_baseline_y);
1713
0
    if (!ItemAdd(bb, id))
1714
0
        return;
1715
1716
0
    const float sep1_x1 = pos.x;
1717
0
    const float sep2_x2 = bb.Max.x;
1718
0
    const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);
1719
1720
0
    const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);
1721
0
    const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN
1722
1723
    // This allows using SameLine() to position something in the 'extra_w'
1724
0
    window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x;
1725
1726
0
    const ImU32 separator_col = GetColorU32(ImGuiCol_Separator);
1727
0
    if (label_size.x > 0.0f)
1728
0
    {
1729
0
        const float sep1_x2 = label_pos.x - style.ItemSpacing.x;
1730
0
        const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x;
1731
0
        if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f)
1732
0
            window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness);
1733
0
        if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f)
1734
0
            window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
1735
0
        if (g.LogEnabled)
1736
0
            LogSetNextTextDecoration("---", NULL);
1737
0
        RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);
1738
0
    }
1739
0
    else
1740
0
    {
1741
0
        if (g.LogEnabled)
1742
0
            LogText("---");
1743
0
        if (separator_thickness > 0.0f)
1744
0
            window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
1745
0
    }
1746
0
}
1747
1748
void ImGui::SeparatorText(const char* label)
1749
0
{
1750
0
    ImGuiWindow* window = GetCurrentWindow();
1751
0
    if (window->SkipItems)
1752
0
        return;
1753
1754
    // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want:
1755
    // - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight)
1756
    // - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string)
1757
    // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...'
1758
    // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item,
1759
    // and then we can turn this into a format function.
1760
0
    SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f);
1761
0
}
1762
1763
// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.
1764
bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col)
1765
0
{
1766
0
    ImGuiContext& g = *GImGui;
1767
0
    ImGuiWindow* window = g.CurrentWindow;
1768
1769
0
    if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav))
1770
0
        return false;
1771
1772
    // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is
1773
    // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item.
1774
    // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item.
1775
0
    ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren;
1776
0
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1777
0
    button_flags |= ImGuiButtonFlags_AllowOverlap;
1778
0
#endif
1779
1780
0
    bool hovered, held;
1781
0
    ImRect bb_interact = bb;
1782
0
    bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
1783
0
    ButtonBehavior(bb_interact, id, &hovered, &held, button_flags);
1784
0
    if (hovered)
1785
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb
1786
1787
0
    if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))
1788
0
        SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
1789
1790
0
    ImRect bb_render = bb;
1791
0
    if (held)
1792
0
    {
1793
0
        float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis];
1794
1795
        // Minimum pane size
1796
0
        float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
1797
0
        float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);
1798
0
        if (mouse_delta < -size_1_maximum_delta)
1799
0
            mouse_delta = -size_1_maximum_delta;
1800
0
        if (mouse_delta > size_2_maximum_delta)
1801
0
            mouse_delta = size_2_maximum_delta;
1802
1803
        // Apply resize
1804
0
        if (mouse_delta != 0.0f)
1805
0
        {
1806
0
            *size1 = ImMax(*size1 + mouse_delta, min_size1);
1807
0
            *size2 = ImMax(*size2 - mouse_delta, min_size2);
1808
0
            bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
1809
0
            MarkItemEdited(id);
1810
0
        }
1811
0
    }
1812
1813
    // Render at new position
1814
0
    if (bg_col & IM_COL32_A_MASK)
1815
0
        window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f);
1816
0
    const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);
1817
0
    window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f);
1818
1819
0
    return held;
1820
0
}
1821
1822
static int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs)
1823
0
{
1824
0
    const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs;
1825
0
    const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs;
1826
0
    if (int d = (int)(b->Width - a->Width))
1827
0
        return d;
1828
0
    return (b->Index - a->Index);
1829
0
}
1830
1831
// Shrink excess width from a set of item, by removing width from the larger items first.
1832
// Set items Width to -1.0f to disable shrinking this item.
1833
void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess, float width_min)
1834
0
{
1835
0
    if (count == 1)
1836
0
    {
1837
0
        if (items[0].Width >= 0.0f)
1838
0
            items[0].Width = ImMax(items[0].Width - width_excess, width_min);
1839
0
        return;
1840
0
    }
1841
0
    ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer); // Sort largest first, smallest last.
1842
0
    int count_same_width = 1;
1843
0
    while (width_excess > 0.001f && count_same_width < count)
1844
0
    {
1845
0
        while (count_same_width < count && items[0].Width <= items[count_same_width].Width)
1846
0
            count_same_width++;
1847
0
        float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f);
1848
0
        max_width_to_remove_per_item = ImMin(items[0].Width - width_min, max_width_to_remove_per_item);
1849
0
        if (max_width_to_remove_per_item <= 0.0f)
1850
0
            break;
1851
0
        float base_width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item);
1852
0
        for (int item_n = 0; item_n < count_same_width; item_n++)
1853
0
        {
1854
0
            float width_to_remove_for_this_item = ImMin(base_width_to_remove_per_item, items[item_n].Width - width_min);
1855
0
            items[item_n].Width -= width_to_remove_for_this_item;
1856
0
            width_excess -= width_to_remove_for_this_item;
1857
0
        }
1858
0
    }
1859
1860
    // Round width and redistribute remainder
1861
    // Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator.
1862
0
    width_excess = 0.0f;
1863
0
    for (int n = 0; n < count; n++)
1864
0
    {
1865
0
        float width_rounded = ImTrunc(items[n].Width);
1866
0
        width_excess += items[n].Width - width_rounded;
1867
0
        items[n].Width = width_rounded;
1868
0
    }
1869
0
    while (width_excess > 0.0f)
1870
0
        for (int n = 0; n < count && width_excess > 0.0f; n++)
1871
0
        {
1872
0
            float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f);
1873
0
            items[n].Width += width_to_add;
1874
0
            width_excess -= width_to_add;
1875
0
        }
1876
0
}
1877
1878
//-------------------------------------------------------------------------
1879
// [SECTION] Widgets: ComboBox
1880
//-------------------------------------------------------------------------
1881
// - CalcMaxPopupHeightFromItemCount() [Internal]
1882
// - BeginCombo()
1883
// - BeginComboPopup() [Internal]
1884
// - EndCombo()
1885
// - BeginComboPreview() [Internal]
1886
// - EndComboPreview() [Internal]
1887
// - Combo()
1888
//-------------------------------------------------------------------------
1889
1890
static float CalcMaxPopupHeightFromItemCount(int items_count)
1891
0
{
1892
0
    ImGuiContext& g = *GImGui;
1893
0
    if (items_count <= 0)
1894
0
        return FLT_MAX;
1895
0
    return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
1896
0
}
1897
1898
bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
1899
0
{
1900
0
    ImGuiContext& g = *GImGui;
1901
0
    ImGuiWindow* window = GetCurrentWindow();
1902
1903
0
    ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.HasFlags;
1904
0
    g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
1905
0
    if (window->SkipItems)
1906
0
        return false;
1907
1908
0
    const ImGuiStyle& style = g.Style;
1909
0
    const ImGuiID id = window->GetID(label);
1910
0
    IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together
1911
0
    if (flags & ImGuiComboFlags_WidthFitPreview)
1912
0
        IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0);
1913
1914
0
    const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
1915
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
1916
0
    const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;
1917
0
    const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
1918
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
1919
0
    const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
1920
0
    ItemSize(total_bb, style.FramePadding.y);
1921
0
    if (!ItemAdd(total_bb, id, &bb))
1922
0
        return false;
1923
1924
    // Open on click
1925
0
    bool hovered, held;
1926
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held);
1927
0
    const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id);
1928
0
    bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);
1929
0
    if (pressed && !popup_open)
1930
0
    {
1931
0
        OpenPopupEx(popup_id, ImGuiPopupFlags_None);
1932
0
        popup_open = true;
1933
0
    }
1934
1935
    // Render shape
1936
0
    const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
1937
0
    const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);
1938
0
    RenderNavCursor(bb, id);
1939
0
    if (!(flags & ImGuiComboFlags_NoPreview))
1940
0
        window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);
1941
0
    if (!(flags & ImGuiComboFlags_NoArrowButton))
1942
0
    {
1943
0
        ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
1944
0
        ImU32 text_col = GetColorU32(ImGuiCol_Text);
1945
0
        window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);
1946
0
        if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x)
1947
0
            RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);
1948
0
    }
1949
0
    RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding);
1950
1951
    // Custom preview
1952
0
    if (flags & ImGuiComboFlags_CustomPreview)
1953
0
    {
1954
0
        g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y);
1955
0
        IM_ASSERT(preview_value == NULL || preview_value[0] == 0);
1956
0
        preview_value = NULL;
1957
0
    }
1958
1959
    // Render preview and label
1960
0
    if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
1961
0
    {
1962
0
        if (g.LogEnabled)
1963
0
            LogSetNextTextDecoration("{", "}");
1964
0
        RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL);
1965
0
    }
1966
0
    if (label_size.x > 0)
1967
0
        RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);
1968
1969
0
    if (!popup_open)
1970
0
        return false;
1971
1972
0
    g.NextWindowData.HasFlags = backup_next_window_data_flags;
1973
0
    return BeginComboPopup(popup_id, bb, flags);
1974
0
}
1975
1976
bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags)
1977
0
{
1978
0
    ImGuiContext& g = *GImGui;
1979
0
    if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None))
1980
0
    {
1981
0
        g.NextWindowData.ClearFlags();
1982
0
        return false;
1983
0
    }
1984
1985
    // Set popup size
1986
0
    float w = bb.GetWidth();
1987
0
    if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint)
1988
0
    {
1989
0
        g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
1990
0
    }
1991
0
    else
1992
0
    {
1993
0
        if ((flags & ImGuiComboFlags_HeightMask_) == 0)
1994
0
            flags |= ImGuiComboFlags_HeightRegular;
1995
0
        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one
1996
0
        int popup_max_height_in_items = -1;
1997
0
        if (flags & ImGuiComboFlags_HeightRegular)     popup_max_height_in_items = 8;
1998
0
        else if (flags & ImGuiComboFlags_HeightSmall)  popup_max_height_in_items = 4;
1999
0
        else if (flags & ImGuiComboFlags_HeightLarge)  popup_max_height_in_items = 20;
2000
0
        ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX);
2001
0
        if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size
2002
0
            constraint_min.x = w;
2003
0
        if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f)
2004
0
            constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);
2005
0
        SetNextWindowSizeConstraints(constraint_min, constraint_max);
2006
0
    }
2007
2008
    // This is essentially a specialized version of BeginPopupEx()
2009
0
    char name[16];
2010
0
    ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth
2011
2012
    // Set position given a custom constraint (peak into expected window size so we can position it)
2013
    // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function?
2014
    // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()?
2015
0
    if (ImGuiWindow* popup_window = FindWindowByName(name))
2016
0
        if (popup_window->WasActive)
2017
0
        {
2018
            // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us.
2019
0
            ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window);
2020
0
            popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)"
2021
0
            ImRect r_outer = GetPopupAllowedExtentRect(popup_window);
2022
0
            ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox);
2023
0
            SetNextWindowPos(pos);
2024
0
        }
2025
2026
    // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx()
2027
0
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove;
2028
0
    PushStyleVarX(ImGuiStyleVar_WindowPadding, g.Style.FramePadding.x); // Horizontally align ourselves with the framed text
2029
0
    bool ret = Begin(name, NULL, window_flags);
2030
0
    PopStyleVar();
2031
0
    if (!ret)
2032
0
    {
2033
0
        EndPopup();
2034
0
        if (!g.IO.ConfigDebugBeginReturnValueOnce && !g.IO.ConfigDebugBeginReturnValueLoop) // Begin may only return false with those debug tools activated.
2035
0
            IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
2036
0
        return false;
2037
0
    }
2038
0
    g.BeginComboDepth++;
2039
0
    return true;
2040
0
}
2041
2042
void ImGui::EndCombo()
2043
0
{
2044
0
    ImGuiContext& g = *GImGui;
2045
0
    EndPopup();
2046
0
    g.BeginComboDepth--;
2047
0
}
2048
2049
// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements
2050
// (Experimental, see GitHub issues: #1658, #4168)
2051
bool ImGui::BeginComboPreview()
2052
0
{
2053
0
    ImGuiContext& g = *GImGui;
2054
0
    ImGuiWindow* window = g.CurrentWindow;
2055
0
    ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;
2056
2057
0
    if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible))
2058
0
        return false;
2059
0
    IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag?
2060
0
    if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional)
2061
0
        return false;
2062
2063
    // FIXME: This could be contained in a PushWorkRect() api
2064
0
    preview_data->BackupCursorPos = window->DC.CursorPos;
2065
0
    preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos;
2066
0
    preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;
2067
0
    preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;
2068
0
    preview_data->BackupLayout = window->DC.LayoutType;
2069
0
    window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding;
2070
0
    window->DC.CursorMaxPos = window->DC.CursorPos;
2071
0
    window->DC.LayoutType = ImGuiLayoutType_Horizontal;
2072
0
    window->DC.IsSameLine = false;
2073
0
    PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true);
2074
2075
0
    return true;
2076
0
}
2077
2078
void ImGui::EndComboPreview()
2079
0
{
2080
0
    ImGuiContext& g = *GImGui;
2081
0
    ImGuiWindow* window = g.CurrentWindow;
2082
0
    ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;
2083
2084
    // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future
2085
0
    ImDrawList* draw_list = window->DrawList;
2086
0
    if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y)
2087
0
        if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command
2088
0
        {
2089
0
            draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect;
2090
0
            draw_list->_TryMergeDrawCmds();
2091
0
        }
2092
0
    PopClipRect();
2093
0
    window->DC.CursorPos = preview_data->BackupCursorPos;
2094
0
    window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos);
2095
0
    window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine;
2096
0
    window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset;
2097
0
    window->DC.LayoutType = preview_data->BackupLayout;
2098
0
    window->DC.IsSameLine = false;
2099
0
    preview_data->PreviewRect = ImRect();
2100
0
}
2101
2102
// Getter for the old Combo() API: const char*[]
2103
static const char* Items_ArrayGetter(void* data, int idx)
2104
0
{
2105
0
    const char* const* items = (const char* const*)data;
2106
0
    return items[idx];
2107
0
}
2108
2109
// Getter for the old Combo() API: "item1\0item2\0item3\0"
2110
static const char* Items_SingleStringGetter(void* data, int idx)
2111
0
{
2112
0
    const char* items_separated_by_zeros = (const char*)data;
2113
0
    int items_count = 0;
2114
0
    const char* p = items_separated_by_zeros;
2115
0
    while (*p)
2116
0
    {
2117
0
        if (idx == items_count)
2118
0
            break;
2119
0
        p += ImStrlen(p) + 1;
2120
0
        items_count++;
2121
0
    }
2122
0
    return *p ? p : NULL;
2123
0
}
2124
2125
// Old API, prefer using BeginCombo() nowadays if you can.
2126
bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items)
2127
0
{
2128
0
    ImGuiContext& g = *GImGui;
2129
2130
    // Call the getter to obtain the preview string which is a parameter to BeginCombo()
2131
0
    const char* preview_value = NULL;
2132
0
    if (*current_item >= 0 && *current_item < items_count)
2133
0
        preview_value = getter(user_data, *current_item);
2134
2135
    // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.
2136
0
    if (popup_max_height_in_items != -1 && !(g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasSizeConstraint))
2137
0
        SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
2138
2139
0
    if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
2140
0
        return false;
2141
2142
    // Display items
2143
0
    bool value_changed = false;
2144
0
    ImGuiListClipper clipper;
2145
0
    clipper.Begin(items_count);
2146
0
    clipper.IncludeItemByIndex(*current_item);
2147
0
    while (clipper.Step())
2148
0
        for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
2149
0
        {
2150
0
            const char* item_text = getter(user_data, i);
2151
0
            if (item_text == NULL)
2152
0
                item_text = "*Unknown item*";
2153
2154
0
            PushID(i);
2155
0
            const bool item_selected = (i == *current_item);
2156
0
            if (Selectable(item_text, item_selected) && *current_item != i)
2157
0
            {
2158
0
                value_changed = true;
2159
0
                *current_item = i;
2160
0
            }
2161
0
            if (item_selected)
2162
0
                SetItemDefaultFocus();
2163
0
            PopID();
2164
0
        }
2165
2166
0
    EndCombo();
2167
0
    if (value_changed)
2168
0
        MarkItemEdited(g.LastItemData.ID);
2169
2170
0
    return value_changed;
2171
0
}
2172
2173
// Combo box helper allowing to pass an array of strings.
2174
bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
2175
0
{
2176
0
    const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
2177
0
    return value_changed;
2178
0
}
2179
2180
// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0"
2181
bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
2182
0
{
2183
0
    int items_count = 0;
2184
0
    const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open
2185
0
    while (*p)
2186
0
    {
2187
0
        p += ImStrlen(p) + 1;
2188
0
        items_count++;
2189
0
    }
2190
0
    bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
2191
0
    return value_changed;
2192
0
}
2193
2194
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
2195
2196
struct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); };
2197
static const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx)
2198
0
{
2199
0
    ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data;
2200
0
    const char* s = NULL;
2201
0
    data->OldCallback(data->UserData, idx, &s);
2202
0
    return s;
2203
0
}
2204
2205
bool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items)
2206
0
{
2207
0
    ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
2208
0
    return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items);
2209
0
}
2210
bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items)
2211
0
{
2212
0
    ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
2213
0
    return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items);
2214
0
}
2215
2216
#endif
2217
2218
//-------------------------------------------------------------------------
2219
// [SECTION] Data Type and Data Formatting Helpers [Internal]
2220
//-------------------------------------------------------------------------
2221
// - DataTypeGetInfo()
2222
// - DataTypeFormatString()
2223
// - DataTypeApplyOp()
2224
// - DataTypeApplyFromText()
2225
// - DataTypeCompare()
2226
// - DataTypeClamp()
2227
// - GetMinimumStepAtDecimalPrecision
2228
// - RoundScalarWithFormat<>()
2229
//-------------------------------------------------------------------------
2230
2231
static const ImGuiDataTypeInfo GDataTypeInfo[] =
2232
{
2233
    { sizeof(char),             "S8",   "%d",   "%d"    },  // ImGuiDataType_S8
2234
    { sizeof(unsigned char),    "U8",   "%u",   "%u"    },
2235
    { sizeof(short),            "S16",  "%d",   "%d"    },  // ImGuiDataType_S16
2236
    { sizeof(unsigned short),   "U16",  "%u",   "%u"    },
2237
    { sizeof(int),              "S32",  "%d",   "%d"    },  // ImGuiDataType_S32
2238
    { sizeof(unsigned int),     "U32",  "%u",   "%u"    },
2239
#ifdef _MSC_VER
2240
    { sizeof(ImS64),            "S64",  "%I64d","%I64d" },  // ImGuiDataType_S64
2241
    { sizeof(ImU64),            "U64",  "%I64u","%I64u" },
2242
#else
2243
    { sizeof(ImS64),            "S64",  "%lld", "%lld"  },  // ImGuiDataType_S64
2244
    { sizeof(ImU64),            "U64",  "%llu", "%llu"  },
2245
#endif
2246
    { sizeof(float),            "float", "%.3f","%f"    },  // ImGuiDataType_Float (float are promoted to double in va_arg)
2247
    { sizeof(double),           "double","%f",  "%lf"   },  // ImGuiDataType_Double
2248
    { sizeof(bool),             "bool", "%d",   "%d"    },  // ImGuiDataType_Bool
2249
    { 0,                        "char*","%s",   "%s"    },  // ImGuiDataType_String
2250
};
2251
IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);
2252
2253
const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type)
2254
0
{
2255
0
    IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
2256
0
    return &GDataTypeInfo[data_type];
2257
0
}
2258
2259
int ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format)
2260
0
{
2261
    // Signedness doesn't matter when pushing integer arguments
2262
0
    if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32)
2263
0
        return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data);
2264
0
    if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)
2265
0
        return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data);
2266
0
    if (data_type == ImGuiDataType_Float)
2267
0
        return ImFormatString(buf, buf_size, format, *(const float*)p_data);
2268
0
    if (data_type == ImGuiDataType_Double)
2269
0
        return ImFormatString(buf, buf_size, format, *(const double*)p_data);
2270
0
    if (data_type == ImGuiDataType_S8)
2271
0
        return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data);
2272
0
    if (data_type == ImGuiDataType_U8)
2273
0
        return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data);
2274
0
    if (data_type == ImGuiDataType_S16)
2275
0
        return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data);
2276
0
    if (data_type == ImGuiDataType_U16)
2277
0
        return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data);
2278
0
    IM_ASSERT(0);
2279
0
    return 0;
2280
0
}
2281
2282
void ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg1, const void* arg2)
2283
0
{
2284
0
    IM_ASSERT(op == '+' || op == '-');
2285
0
    switch (data_type)
2286
0
    {
2287
0
        case ImGuiDataType_S8:
2288
0
            if (op == '+') { *(ImS8*)output  = ImAddClampOverflow(*(const ImS8*)arg1,  *(const ImS8*)arg2,  IM_S8_MIN,  IM_S8_MAX); }
2289
0
            if (op == '-') { *(ImS8*)output  = ImSubClampOverflow(*(const ImS8*)arg1,  *(const ImS8*)arg2,  IM_S8_MIN,  IM_S8_MAX); }
2290
0
            return;
2291
0
        case ImGuiDataType_U8:
2292
0
            if (op == '+') { *(ImU8*)output  = ImAddClampOverflow(*(const ImU8*)arg1,  *(const ImU8*)arg2,  IM_U8_MIN,  IM_U8_MAX); }
2293
0
            if (op == '-') { *(ImU8*)output  = ImSubClampOverflow(*(const ImU8*)arg1,  *(const ImU8*)arg2,  IM_U8_MIN,  IM_U8_MAX); }
2294
0
            return;
2295
0
        case ImGuiDataType_S16:
2296
0
            if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }
2297
0
            if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }
2298
0
            return;
2299
0
        case ImGuiDataType_U16:
2300
0
            if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }
2301
0
            if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }
2302
0
            return;
2303
0
        case ImGuiDataType_S32:
2304
0
            if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }
2305
0
            if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }
2306
0
            return;
2307
0
        case ImGuiDataType_U32:
2308
0
            if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }
2309
0
            if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }
2310
0
            return;
2311
0
        case ImGuiDataType_S64:
2312
0
            if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }
2313
0
            if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }
2314
0
            return;
2315
0
        case ImGuiDataType_U64:
2316
0
            if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }
2317
0
            if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }
2318
0
            return;
2319
0
        case ImGuiDataType_Float:
2320
0
            if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; }
2321
0
            if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; }
2322
0
            return;
2323
0
        case ImGuiDataType_Double:
2324
0
            if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; }
2325
0
            if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; }
2326
0
            return;
2327
0
        case ImGuiDataType_COUNT: break;
2328
0
    }
2329
0
    IM_ASSERT(0);
2330
0
}
2331
2332
// User can input math operators (e.g. +100) to edit a numerical values.
2333
// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..
2334
bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format, void* p_data_when_empty)
2335
0
{
2336
    // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.
2337
0
    const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);
2338
0
    ImGuiDataTypeStorage data_backup;
2339
0
    memcpy(&data_backup, p_data, type_info->Size);
2340
2341
0
    while (ImCharIsBlankA(*buf))
2342
0
        buf++;
2343
0
    if (!buf[0])
2344
0
    {
2345
0
        if (p_data_when_empty != NULL)
2346
0
        {
2347
0
            memcpy(p_data, p_data_when_empty, type_info->Size);
2348
0
            return memcmp(&data_backup, p_data, type_info->Size) != 0;
2349
0
        }
2350
0
        return false;
2351
0
    }
2352
2353
    // Sanitize format
2354
    // - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf
2355
    // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %.
2356
0
    char format_sanitized[32];
2357
0
    if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)
2358
0
        format = type_info->ScanFmt;
2359
0
    else
2360
0
        format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized));
2361
2362
    // Small types need a 32-bit buffer to receive the result from scanf()
2363
0
    int v32 = 0;
2364
0
    if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1)
2365
0
        return false;
2366
0
    if (type_info->Size < 4)
2367
0
    {
2368
0
        if (data_type == ImGuiDataType_S8)
2369
0
            *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX);
2370
0
        else if (data_type == ImGuiDataType_U8)
2371
0
            *(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX);
2372
0
        else if (data_type == ImGuiDataType_S16)
2373
0
            *(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX);
2374
0
        else if (data_type == ImGuiDataType_U16)
2375
0
            *(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX);
2376
0
        else
2377
0
            IM_ASSERT(0);
2378
0
    }
2379
2380
0
    return memcmp(&data_backup, p_data, type_info->Size) != 0;
2381
0
}
2382
2383
template<typename T>
2384
static int DataTypeCompareT(const T* lhs, const T* rhs)
2385
0
{
2386
0
    if (*lhs < *rhs) return -1;
2387
0
    if (*lhs > *rhs) return +1;
2388
0
    return 0;
2389
0
}
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIaEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIhEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIsEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTItEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIiEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIjEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIxEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIyEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIfEiPKT_S2_
Unexecuted instantiation: imgui_widgets.cpp:_ZL16DataTypeCompareTIdEiPKT_S2_
2390
2391
int ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2)
2392
0
{
2393
0
    switch (data_type)
2394
0
    {
2395
0
    case ImGuiDataType_S8:     return DataTypeCompareT<ImS8  >((const ImS8*  )arg_1, (const ImS8*  )arg_2);
2396
0
    case ImGuiDataType_U8:     return DataTypeCompareT<ImU8  >((const ImU8*  )arg_1, (const ImU8*  )arg_2);
2397
0
    case ImGuiDataType_S16:    return DataTypeCompareT<ImS16 >((const ImS16* )arg_1, (const ImS16* )arg_2);
2398
0
    case ImGuiDataType_U16:    return DataTypeCompareT<ImU16 >((const ImU16* )arg_1, (const ImU16* )arg_2);
2399
0
    case ImGuiDataType_S32:    return DataTypeCompareT<ImS32 >((const ImS32* )arg_1, (const ImS32* )arg_2);
2400
0
    case ImGuiDataType_U32:    return DataTypeCompareT<ImU32 >((const ImU32* )arg_1, (const ImU32* )arg_2);
2401
0
    case ImGuiDataType_S64:    return DataTypeCompareT<ImS64 >((const ImS64* )arg_1, (const ImS64* )arg_2);
2402
0
    case ImGuiDataType_U64:    return DataTypeCompareT<ImU64 >((const ImU64* )arg_1, (const ImU64* )arg_2);
2403
0
    case ImGuiDataType_Float:  return DataTypeCompareT<float >((const float* )arg_1, (const float* )arg_2);
2404
0
    case ImGuiDataType_Double: return DataTypeCompareT<double>((const double*)arg_1, (const double*)arg_2);
2405
0
    case ImGuiDataType_COUNT:  break;
2406
0
    }
2407
0
    IM_ASSERT(0);
2408
0
    return 0;
2409
0
}
2410
2411
template<typename T>
2412
static bool DataTypeClampT(T* v, const T* v_min, const T* v_max)
2413
0
{
2414
    // Clamp, both sides are optional, return true if modified
2415
0
    if (v_min && *v < *v_min) { *v = *v_min; return true; }
2416
0
    if (v_max && *v > *v_max) { *v = *v_max; return true; }
2417
0
    return false;
2418
0
}
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIaEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIhEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIsEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTItEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIiEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIjEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIxEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIyEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIfEbPT_PKS0_S3_
Unexecuted instantiation: imgui_widgets.cpp:_ZL14DataTypeClampTIdEbPT_PKS0_S3_
2419
2420
bool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max)
2421
0
{
2422
0
    switch (data_type)
2423
0
    {
2424
0
    case ImGuiDataType_S8:     return DataTypeClampT<ImS8  >((ImS8*  )p_data, (const ImS8*  )p_min, (const ImS8*  )p_max);
2425
0
    case ImGuiDataType_U8:     return DataTypeClampT<ImU8  >((ImU8*  )p_data, (const ImU8*  )p_min, (const ImU8*  )p_max);
2426
0
    case ImGuiDataType_S16:    return DataTypeClampT<ImS16 >((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max);
2427
0
    case ImGuiDataType_U16:    return DataTypeClampT<ImU16 >((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max);
2428
0
    case ImGuiDataType_S32:    return DataTypeClampT<ImS32 >((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max);
2429
0
    case ImGuiDataType_U32:    return DataTypeClampT<ImU32 >((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max);
2430
0
    case ImGuiDataType_S64:    return DataTypeClampT<ImS64 >((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max);
2431
0
    case ImGuiDataType_U64:    return DataTypeClampT<ImU64 >((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max);
2432
0
    case ImGuiDataType_Float:  return DataTypeClampT<float >((float* )p_data, (const float* )p_min, (const float* )p_max);
2433
0
    case ImGuiDataType_Double: return DataTypeClampT<double>((double*)p_data, (const double*)p_min, (const double*)p_max);
2434
0
    case ImGuiDataType_COUNT:  break;
2435
0
    }
2436
0
    IM_ASSERT(0);
2437
0
    return false;
2438
0
}
2439
2440
bool ImGui::DataTypeIsZero(ImGuiDataType data_type, const void* p_data)
2441
0
{
2442
0
    ImGuiContext& g = *GImGui;
2443
0
    return DataTypeCompare(data_type, p_data, &g.DataTypeZeroValue) == 0;
2444
0
}
2445
2446
static float GetMinimumStepAtDecimalPrecision(int decimal_precision)
2447
0
{
2448
0
    static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };
2449
0
    if (decimal_precision < 0)
2450
0
        return FLT_MIN;
2451
0
    return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);
2452
0
}
2453
2454
template<typename TYPE>
2455
TYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)
2456
0
{
2457
0
    IM_UNUSED(data_type);
2458
0
    IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);
2459
0
    const char* fmt_start = ImParseFormatFindStart(format);
2460
0
    if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string
2461
0
        return v;
2462
2463
    // Sanitize format
2464
0
    char fmt_sanitized[32];
2465
0
    ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));
2466
0
    fmt_start = fmt_sanitized;
2467
2468
    // Format value with our rounding, and read back
2469
0
    char v_str[64];
2470
0
    ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);
2471
0
    const char* p = v_str;
2472
0
    while (*p == ' ')
2473
0
        p++;
2474
0
    v = (TYPE)ImAtof(p);
2475
2476
0
    return v;
2477
0
}
Unexecuted instantiation: _ZN5ImGui22RoundScalarWithFormatTIiEET_PKciS1_
Unexecuted instantiation: _ZN5ImGui22RoundScalarWithFormatTIjEET_PKciS1_
Unexecuted instantiation: _ZN5ImGui22RoundScalarWithFormatTIxEET_PKciS1_
Unexecuted instantiation: _ZN5ImGui22RoundScalarWithFormatTIyEET_PKciS1_
Unexecuted instantiation: _ZN5ImGui22RoundScalarWithFormatTIfEET_PKciS1_
Unexecuted instantiation: _ZN5ImGui22RoundScalarWithFormatTIdEET_PKciS1_
2478
2479
//-------------------------------------------------------------------------
2480
// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.
2481
//-------------------------------------------------------------------------
2482
// - DragBehaviorT<>() [Internal]
2483
// - DragBehavior() [Internal]
2484
// - DragScalar()
2485
// - DragScalarN()
2486
// - DragFloat()
2487
// - DragFloat2()
2488
// - DragFloat3()
2489
// - DragFloat4()
2490
// - DragFloatRange2()
2491
// - DragInt()
2492
// - DragInt2()
2493
// - DragInt3()
2494
// - DragInt4()
2495
// - DragIntRange2()
2496
//-------------------------------------------------------------------------
2497
2498
// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)
2499
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
2500
bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags)
2501
0
{
2502
0
    ImGuiContext& g = *GImGui;
2503
0
    const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
2504
0
    const bool is_bounded = (v_min < v_max) || ((v_min == v_max) && (v_min != 0.0f || (flags & ImGuiSliderFlags_ClampZeroRange)));
2505
0
    const bool is_wrapped = is_bounded && (flags & ImGuiSliderFlags_WrapAround);
2506
0
    const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
2507
0
    const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
2508
2509
    // Default tweak speed
2510
0
    if (v_speed == 0.0f && is_bounded && (v_max - v_min < FLT_MAX))
2511
0
        v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);
2512
2513
    // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings
2514
0
    float adjust_delta = 0.0f;
2515
0
    if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
2516
0
    {
2517
0
        adjust_delta = g.IO.MouseDelta[axis];
2518
0
        if (g.IO.KeyAlt && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
2519
0
            adjust_delta *= 1.0f / 100.0f;
2520
0
        if (g.IO.KeyShift && !(flags & ImGuiSliderFlags_NoSpeedTweaks))
2521
0
            adjust_delta *= 10.0f;
2522
0
    }
2523
0
    else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
2524
0
    {
2525
0
        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
2526
0
        const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
2527
0
        const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
2528
0
        const float tweak_factor = (flags & ImGuiSliderFlags_NoSpeedTweaks) ? 1.0f : tweak_slow ? 1.0f / 10.0f : tweak_fast ? 10.0f : 1.0f;
2529
0
        adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;
2530
0
        v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));
2531
0
    }
2532
0
    adjust_delta *= v_speed;
2533
2534
    // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter.
2535
0
    if (axis == ImGuiAxis_Y)
2536
0
        adjust_delta = -adjust_delta;
2537
2538
    // For logarithmic use our range is effectively 0..1 so scale the delta into that range
2539
0
    if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0
2540
0
        adjust_delta /= (float)(v_max - v_min);
2541
2542
    // Clear current value on activation
2543
    // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.
2544
0
    const bool is_just_activated = g.ActiveIdIsJustActivated;
2545
0
    const bool is_already_past_limits_and_pushing_outward = is_bounded && !is_wrapped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));
2546
0
    if (is_just_activated || is_already_past_limits_and_pushing_outward)
2547
0
    {
2548
0
        g.DragCurrentAccum = 0.0f;
2549
0
        g.DragCurrentAccumDirty = false;
2550
0
    }
2551
0
    else if (adjust_delta != 0.0f)
2552
0
    {
2553
0
        g.DragCurrentAccum += adjust_delta;
2554
0
        g.DragCurrentAccumDirty = true;
2555
0
    }
2556
2557
0
    if (!g.DragCurrentAccumDirty)
2558
0
        return false;
2559
2560
0
    TYPE v_cur = *v;
2561
0
    FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;
2562
2563
0
    float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true
2564
0
    const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense)
2565
0
    if (is_logarithmic)
2566
0
    {
2567
        // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
2568
0
        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;
2569
0
        logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);
2570
2571
        // Convert to parametric space, apply delta, convert back
2572
0
        float v_old_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
2573
0
        float v_new_parametric = v_old_parametric + g.DragCurrentAccum;
2574
0
        v_cur = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
2575
0
        v_old_ref_for_accum_remainder = v_old_parametric;
2576
0
    }
2577
0
    else
2578
0
    {
2579
0
        v_cur += (SIGNEDTYPE)g.DragCurrentAccum;
2580
0
    }
2581
2582
    // Round to user desired precision based on format string
2583
0
    if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
2584
0
        v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur);
2585
2586
    // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.
2587
0
    g.DragCurrentAccumDirty = false;
2588
0
    if (is_logarithmic)
2589
0
    {
2590
        // Convert to parametric space, apply delta, convert back
2591
0
        float v_new_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
2592
0
        g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder);
2593
0
    }
2594
0
    else
2595
0
    {
2596
0
        g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);
2597
0
    }
2598
2599
    // Lose zero sign for float/double
2600
0
    if (v_cur == (TYPE)-0)
2601
0
        v_cur = (TYPE)0;
2602
2603
0
    if (*v != v_cur && is_bounded)
2604
0
    {
2605
0
        if (is_wrapped)
2606
0
        {
2607
            // Wrap values
2608
0
            if (v_cur < v_min)
2609
0
                v_cur += v_max - v_min + (is_floating_point ? 0 : 1);
2610
0
            if (v_cur > v_max)
2611
0
                v_cur -= v_max - v_min + (is_floating_point ? 0 : 1);
2612
0
        }
2613
0
        else
2614
0
        {
2615
            // Clamp values + handle overflow/wrap-around for integer types.
2616
0
            if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point))
2617
0
                v_cur = v_min;
2618
0
            if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point))
2619
0
                v_cur = v_max;
2620
0
        }
2621
0
    }
2622
2623
    // Apply result
2624
0
    if (*v == v_cur)
2625
0
        return false;
2626
0
    *v = v_cur;
2627
0
    return true;
2628
0
}
Unexecuted instantiation: _ZN5ImGui13DragBehaviorTIiifEEbiPT_fS1_S1_PKci
Unexecuted instantiation: _ZN5ImGui13DragBehaviorTIjifEEbiPT_fS1_S1_PKci
Unexecuted instantiation: _ZN5ImGui13DragBehaviorTIxxdEEbiPT_fS1_S1_PKci
Unexecuted instantiation: _ZN5ImGui13DragBehaviorTIyxdEEbiPT_fS1_S1_PKci
Unexecuted instantiation: _ZN5ImGui13DragBehaviorTIfffEEbiPT_fS1_S1_PKci
Unexecuted instantiation: _ZN5ImGui13DragBehaviorTIdddEEbiPT_fS1_S1_PKci
2629
2630
bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
2631
0
{
2632
    // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
2633
0
    IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
2634
2635
0
    ImGuiContext& g = *GImGui;
2636
0
    if (g.ActiveId == id)
2637
0
    {
2638
        // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation.
2639
0
        if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])
2640
0
            ClearActiveID();
2641
0
        else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
2642
0
            ClearActiveID();
2643
0
    }
2644
0
    if (g.ActiveId != id)
2645
0
        return false;
2646
0
    if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
2647
0
        return false;
2648
2649
0
    switch (data_type)
2650
0
    {
2651
0
    case ImGuiDataType_S8:     { ImS32 v32 = (ImS32)*(ImS8*)p_v;  bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN,  p_max ? *(const ImS8*)p_max  : IM_S8_MAX,  format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }
2652
0
    case ImGuiDataType_U8:     { ImU32 v32 = (ImU32)*(ImU8*)p_v;  bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN,  p_max ? *(const ImU8*)p_max  : IM_U8_MAX,  format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }
2653
0
    case ImGuiDataType_S16:    { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
2654
0
    case ImGuiDataType_U16:    { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
2655
0
    case ImGuiDataType_S32:    return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v,  v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags);
2656
0
    case ImGuiDataType_U32:    return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v,  v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags);
2657
0
    case ImGuiDataType_S64:    return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v,  v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags);
2658
0
    case ImGuiDataType_U64:    return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v,  v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags);
2659
0
    case ImGuiDataType_Float:  return DragBehaviorT<float, float, float >(data_type, (float*)p_v,  v_speed, p_min ? *(const float* )p_min : -FLT_MAX,   p_max ? *(const float* )p_max : FLT_MAX,    format, flags);
2660
0
    case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX,   p_max ? *(const double*)p_max : DBL_MAX,    format, flags);
2661
0
    case ImGuiDataType_COUNT:  break;
2662
0
    }
2663
0
    IM_ASSERT(0);
2664
0
    return false;
2665
0
}
2666
2667
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional.
2668
// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
2669
bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
2670
0
{
2671
0
    ImGuiWindow* window = GetCurrentWindow();
2672
0
    if (window->SkipItems)
2673
0
        return false;
2674
2675
0
    ImGuiContext& g = *GImGui;
2676
0
    const ImGuiStyle& style = g.Style;
2677
0
    const ImGuiID id = window->GetID(label);
2678
0
    const float w = CalcItemWidth();
2679
2680
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
2681
0
    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
2682
0
    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
2683
2684
0
    const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
2685
0
    ItemSize(total_bb, style.FramePadding.y);
2686
0
    if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
2687
0
        return false;
2688
2689
    // Default format string when passing NULL
2690
0
    if (format == NULL)
2691
0
        format = DataTypeGetInfo(data_type)->PrintFmt;
2692
2693
0
    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
2694
0
    bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
2695
0
    if (!temp_input_is_active)
2696
0
    {
2697
        // Tabbing or CTRL+click on Drag turns it into an InputText
2698
0
        const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
2699
0
        const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));
2700
0
        const bool make_active = (clicked || double_clicked || g.NavActivateId == id);
2701
0
        if (make_active && (clicked || double_clicked))
2702
0
            SetKeyOwner(ImGuiKey_MouseLeft, id);
2703
0
        if (make_active && temp_input_allowed)
2704
0
            if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
2705
0
                temp_input_is_active = true;
2706
2707
        // (Optional) simple click (without moving) turns Drag into an InputText
2708
0
        if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active)
2709
0
            if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))
2710
0
            {
2711
0
                g.NavActivateId = id;
2712
0
                g.NavActivateFlags = ImGuiActivateFlags_PreferInput;
2713
0
                temp_input_is_active = true;
2714
0
            }
2715
2716
        // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
2717
0
        if (make_active)
2718
0
            memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
2719
2720
0
        if (make_active && !temp_input_is_active)
2721
0
        {
2722
0
            SetActiveID(id, window);
2723
0
            SetFocusID(id, window);
2724
0
            FocusWindow(window);
2725
0
            g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
2726
0
        }
2727
0
    }
2728
2729
0
    if (temp_input_is_active)
2730
0
    {
2731
        // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp)
2732
0
        bool clamp_enabled = false;
2733
0
        if ((flags & ImGuiSliderFlags_ClampOnInput) && (p_min != NULL || p_max != NULL))
2734
0
        {
2735
0
            const int clamp_range_dir = (p_min != NULL && p_max != NULL) ? DataTypeCompare(data_type, p_min, p_max) : 0; // -1 when *p_min < *p_max, == 0 when *p_min == *p_max
2736
0
            if (p_min == NULL || p_max == NULL || clamp_range_dir < 0)
2737
0
                clamp_enabled = true;
2738
0
            else if (clamp_range_dir == 0)
2739
0
                clamp_enabled = DataTypeIsZero(data_type, p_min) ? ((flags & ImGuiSliderFlags_ClampZeroRange) != 0) : true;
2740
0
        }
2741
0
        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);
2742
0
    }
2743
2744
    // Draw frame
2745
0
    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
2746
0
    RenderNavCursor(frame_bb, id);
2747
0
    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);
2748
2749
    // Drag behavior
2750
0
    const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags);
2751
0
    if (value_changed)
2752
0
        MarkItemEdited(id);
2753
2754
    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
2755
0
    char value_buf[64];
2756
0
    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
2757
0
    if (g.LogEnabled)
2758
0
        LogSetNextTextDecoration("{", "}");
2759
0
    RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
2760
2761
0
    if (label_size.x > 0.0f)
2762
0
        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
2763
2764
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
2765
0
    return value_changed;
2766
0
}
2767
2768
bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
2769
0
{
2770
0
    ImGuiWindow* window = GetCurrentWindow();
2771
0
    if (window->SkipItems)
2772
0
        return false;
2773
2774
0
    ImGuiContext& g = *GImGui;
2775
0
    bool value_changed = false;
2776
0
    BeginGroup();
2777
0
    PushID(label);
2778
0
    PushMultiItemsWidths(components, CalcItemWidth());
2779
0
    size_t type_size = GDataTypeInfo[data_type].Size;
2780
0
    for (int i = 0; i < components; i++)
2781
0
    {
2782
0
        PushID(i);
2783
0
        if (i > 0)
2784
0
            SameLine(0, g.Style.ItemInnerSpacing.x);
2785
0
        value_changed |= DragScalar("", data_type, p_data, v_speed, p_min, p_max, format, flags);
2786
0
        PopID();
2787
0
        PopItemWidth();
2788
0
        p_data = (void*)((char*)p_data + type_size);
2789
0
    }
2790
0
    PopID();
2791
2792
0
    const char* label_end = FindRenderedTextEnd(label);
2793
0
    if (label != label_end)
2794
0
    {
2795
0
        SameLine(0, g.Style.ItemInnerSpacing.x);
2796
0
        TextEx(label, label_end);
2797
0
    }
2798
2799
0
    EndGroup();
2800
0
    return value_changed;
2801
0
}
2802
2803
bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
2804
0
{
2805
0
    return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags);
2806
0
}
2807
2808
bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
2809
0
{
2810
0
    return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags);
2811
0
}
2812
2813
bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
2814
0
{
2815
0
    return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags);
2816
0
}
2817
2818
bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
2819
0
{
2820
0
    return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags);
2821
0
}
2822
2823
// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.
2824
bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)
2825
0
{
2826
0
    ImGuiWindow* window = GetCurrentWindow();
2827
0
    if (window->SkipItems)
2828
0
        return false;
2829
2830
0
    ImGuiContext& g = *GImGui;
2831
0
    PushID(label);
2832
0
    BeginGroup();
2833
0
    PushMultiItemsWidths(2, CalcItemWidth());
2834
2835
0
    float min_min = (v_min >= v_max) ? -FLT_MAX : v_min;
2836
0
    float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);
2837
0
    ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);
2838
0
    bool value_changed = DragScalar("##min", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags);
2839
0
    PopItemWidth();
2840
0
    SameLine(0, g.Style.ItemInnerSpacing.x);
2841
2842
0
    float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);
2843
0
    float max_max = (v_min >= v_max) ? FLT_MAX : v_max;
2844
0
    ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);
2845
0
    value_changed |= DragScalar("##max", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags);
2846
0
    PopItemWidth();
2847
0
    SameLine(0, g.Style.ItemInnerSpacing.x);
2848
2849
0
    TextEx(label, FindRenderedTextEnd(label));
2850
0
    EndGroup();
2851
0
    PopID();
2852
2853
0
    return value_changed;
2854
0
}
2855
2856
// NB: v_speed is float to allow adjusting the drag speed with more precision
2857
bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
2858
0
{
2859
0
    return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags);
2860
0
}
2861
2862
bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
2863
0
{
2864
0
    return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags);
2865
0
}
2866
2867
bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
2868
0
{
2869
0
    return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags);
2870
0
}
2871
2872
bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
2873
0
{
2874
0
    return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags);
2875
0
}
2876
2877
// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.
2878
bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)
2879
0
{
2880
0
    ImGuiWindow* window = GetCurrentWindow();
2881
0
    if (window->SkipItems)
2882
0
        return false;
2883
2884
0
    ImGuiContext& g = *GImGui;
2885
0
    PushID(label);
2886
0
    BeginGroup();
2887
0
    PushMultiItemsWidths(2, CalcItemWidth());
2888
2889
0
    int min_min = (v_min >= v_max) ? INT_MIN : v_min;
2890
0
    int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);
2891
0
    ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);
2892
0
    bool value_changed = DragInt("##min", v_current_min, v_speed, min_min, min_max, format, min_flags);
2893
0
    PopItemWidth();
2894
0
    SameLine(0, g.Style.ItemInnerSpacing.x);
2895
2896
0
    int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);
2897
0
    int max_max = (v_min >= v_max) ? INT_MAX : v_max;
2898
0
    ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);
2899
0
    value_changed |= DragInt("##max", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags);
2900
0
    PopItemWidth();
2901
0
    SameLine(0, g.Style.ItemInnerSpacing.x);
2902
2903
0
    TextEx(label, FindRenderedTextEnd(label));
2904
0
    EndGroup();
2905
0
    PopID();
2906
2907
0
    return value_changed;
2908
0
}
2909
2910
//-------------------------------------------------------------------------
2911
// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.
2912
//-------------------------------------------------------------------------
2913
// - ScaleRatioFromValueT<> [Internal]
2914
// - ScaleValueFromRatioT<> [Internal]
2915
// - SliderBehaviorT<>() [Internal]
2916
// - SliderBehavior() [Internal]
2917
// - SliderScalar()
2918
// - SliderScalarN()
2919
// - SliderFloat()
2920
// - SliderFloat2()
2921
// - SliderFloat3()
2922
// - SliderFloat4()
2923
// - SliderAngle()
2924
// - SliderInt()
2925
// - SliderInt2()
2926
// - SliderInt3()
2927
// - SliderInt4()
2928
// - VSliderScalar()
2929
// - VSliderFloat()
2930
// - VSliderInt()
2931
//-------------------------------------------------------------------------
2932
2933
// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT)
2934
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
2935
float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)
2936
0
{
2937
0
    if (v_min == v_max)
2938
0
        return 0.0f;
2939
0
    IM_UNUSED(data_type);
2940
2941
0
    const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);
2942
0
    if (is_logarithmic)
2943
0
    {
2944
0
        bool flipped = v_max < v_min;
2945
2946
0
        if (flipped) // Handle the case where the range is backwards
2947
0
            ImSwap(v_min, v_max);
2948
2949
        // Fudge min/max to avoid getting close to log(0)
2950
0
        FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;
2951
0
        FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;
2952
2953
        // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)
2954
0
        if ((v_min == 0.0f) && (v_max < 0.0f))
2955
0
            v_min_fudged = -logarithmic_zero_epsilon;
2956
0
        else if ((v_max == 0.0f) && (v_min < 0.0f))
2957
0
            v_max_fudged = -logarithmic_zero_epsilon;
2958
2959
0
        float result;
2960
0
        if (v_clamped <= v_min_fudged)
2961
0
            result = 0.0f; // Workaround for values that are in-range but below our fudge
2962
0
        else if (v_clamped >= v_max_fudged)
2963
0
            result = 1.0f; // Workaround for values that are in-range but above our fudge
2964
0
        else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions
2965
0
        {
2966
0
            float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space.  There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine)
2967
0
            float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;
2968
0
            float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;
2969
0
            if (v == 0.0f)
2970
0
                result = zero_point_center; // Special case for exactly zero
2971
0
            else if (v < 0.0f)
2972
0
                result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L;
2973
0
            else
2974
0
                result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R));
2975
0
        }
2976
0
        else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider
2977
0
            result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged));
2978
0
        else
2979
0
            result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged));
2980
2981
0
        return flipped ? (1.0f - result) : result;
2982
0
    }
2983
0
    else
2984
0
    {
2985
        // Linear slider
2986
0
        return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min));
2987
0
    }
2988
0
}
Unexecuted instantiation: _ZN5ImGui20ScaleRatioFromValueTIiifEEfiT_S1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleRatioFromValueTIjifEEfiT_S1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleRatioFromValueTIxxdEEfiT_S1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleRatioFromValueTIyxdEEfiT_S1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleRatioFromValueTIfffEEfiT_S1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleRatioFromValueTIdddEEfiT_S1_S1_bff
2989
2990
// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT)
2991
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
2992
TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)
2993
0
{
2994
    // We special-case the extents because otherwise our logarithmic fudging can lead to "mathematically correct"
2995
    // but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler.
2996
0
    if (t <= 0.0f || v_min == v_max)
2997
0
        return v_min;
2998
0
    if (t >= 1.0f)
2999
0
        return v_max;
3000
3001
0
    TYPE result = (TYPE)0;
3002
0
    if (is_logarithmic)
3003
0
    {
3004
        // Fudge min/max to avoid getting silly results close to zero
3005
0
        FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;
3006
0
        FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;
3007
3008
0
        const bool flipped = v_max < v_min; // Check if range is "backwards"
3009
0
        if (flipped)
3010
0
            ImSwap(v_min_fudged, v_max_fudged);
3011
3012
        // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)
3013
0
        if ((v_max == 0.0f) && (v_min < 0.0f))
3014
0
            v_max_fudged = -logarithmic_zero_epsilon;
3015
3016
0
        float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range
3017
3018
0
        if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts
3019
0
        {
3020
0
            float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space
3021
0
            float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;
3022
0
            float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;
3023
0
            if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R)
3024
0
                result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise)
3025
0
            else if (t_with_flip < zero_point_center)
3026
0
                result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L))));
3027
0
            else
3028
0
                result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R))));
3029
0
        }
3030
0
        else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider
3031
0
            result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip)));
3032
0
        else
3033
0
            result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip));
3034
0
    }
3035
0
    else
3036
0
    {
3037
        // Linear slider
3038
0
        const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
3039
0
        if (is_floating_point)
3040
0
        {
3041
0
            result = ImLerp(v_min, v_max, t);
3042
0
        }
3043
0
        else if (t < 1.0)
3044
0
        {
3045
            // - For integer values we want the clicking position to match the grab box so we round above
3046
            //   This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
3047
            // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64
3048
            //   range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits.
3049
0
            FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t;
3050
0
            result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5)));
3051
0
        }
3052
0
    }
3053
3054
0
    return result;
3055
0
}
Unexecuted instantiation: _ZN5ImGui20ScaleValueFromRatioTIiifEET_ifS1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleValueFromRatioTIjifEET_ifS1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleValueFromRatioTIxxdEET_ifS1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleValueFromRatioTIyxdEET_ifS1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleValueFromRatioTIfffEET_ifS1_S1_bff
Unexecuted instantiation: _ZN5ImGui20ScaleValueFromRatioTIdddEET_ifS1_S1_bff
3056
3057
// FIXME: Try to move more of the code into shared SliderBehavior()
3058
template<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>
3059
bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)
3060
0
{
3061
0
    ImGuiContext& g = *GImGui;
3062
0
    const ImGuiStyle& style = g.Style;
3063
3064
0
    const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;
3065
0
    const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;
3066
0
    const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);
3067
0
    const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it.
3068
3069
    // Calculate bounds
3070
0
    const float grab_padding = 2.0f; // FIXME: Should be part of style.
3071
0
    const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;
3072
0
    float grab_sz = style.GrabMinSize;
3073
0
    if (!is_floating_point && v_range_f >= 0.0f)                         // v_range_f < 0 may happen on integer overflows
3074
0
        grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit
3075
0
    grab_sz = ImMin(grab_sz, slider_sz);
3076
0
    const float slider_usable_sz = slider_sz - grab_sz;
3077
0
    const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;
3078
0
    const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f;
3079
3080
0
    float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true
3081
0
    float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true
3082
0
    if (is_logarithmic)
3083
0
    {
3084
        // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.
3085
0
        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;
3086
0
        logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);
3087
0
        zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);
3088
0
    }
3089
3090
    // Process interacting with the slider
3091
0
    bool value_changed = false;
3092
0
    if (g.ActiveId == id)
3093
0
    {
3094
0
        bool set_new_value = false;
3095
0
        float clicked_t = 0.0f;
3096
0
        if (g.ActiveIdSource == ImGuiInputSource_Mouse)
3097
0
        {
3098
0
            if (!g.IO.MouseDown[0])
3099
0
            {
3100
0
                ClearActiveID();
3101
0
            }
3102
0
            else
3103
0
            {
3104
0
                const float mouse_abs_pos = g.IO.MousePos[axis];
3105
0
                if (g.ActiveIdIsJustActivated)
3106
0
                {
3107
0
                    float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3108
0
                    if (axis == ImGuiAxis_Y)
3109
0
                        grab_t = 1.0f - grab_t;
3110
0
                    const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
3111
0
                    const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here.
3112
0
                    g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f;
3113
0
                }
3114
0
                if (slider_usable_sz > 0.0f)
3115
0
                    clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz);
3116
0
                if (axis == ImGuiAxis_Y)
3117
0
                    clicked_t = 1.0f - clicked_t;
3118
0
                set_new_value = true;
3119
0
            }
3120
0
        }
3121
0
        else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)
3122
0
        {
3123
0
            if (g.ActiveIdIsJustActivated)
3124
0
            {
3125
0
                g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation
3126
0
                g.SliderCurrentAccumDirty = false;
3127
0
            }
3128
3129
0
            float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis);
3130
0
            if (input_delta != 0.0f)
3131
0
            {
3132
0
                const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);
3133
0
                const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);
3134
0
                const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;
3135
0
                if (decimal_precision > 0)
3136
0
                {
3137
0
                    input_delta /= 100.0f; // Keyboard/Gamepad tweak speeds in % of slider bounds
3138
0
                    if (tweak_slow)
3139
0
                        input_delta /= 10.0f;
3140
0
                }
3141
0
                else
3142
0
                {
3143
0
                    if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)
3144
0
                        input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Keyboard/Gamepad tweak speeds in integer steps
3145
0
                    else
3146
0
                        input_delta /= 100.0f;
3147
0
                }
3148
0
                if (tweak_fast)
3149
0
                    input_delta *= 10.0f;
3150
3151
0
                g.SliderCurrentAccum += input_delta;
3152
0
                g.SliderCurrentAccumDirty = true;
3153
0
            }
3154
3155
0
            float delta = g.SliderCurrentAccum;
3156
0
            if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)
3157
0
            {
3158
0
                ClearActiveID();
3159
0
            }
3160
0
            else if (g.SliderCurrentAccumDirty)
3161
0
            {
3162
0
                clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3163
3164
0
                if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits
3165
0
                {
3166
0
                    set_new_value = false;
3167
0
                    g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate
3168
0
                }
3169
0
                else
3170
0
                {
3171
0
                    set_new_value = true;
3172
0
                    float old_clicked_t = clicked_t;
3173
0
                    clicked_t = ImSaturate(clicked_t + delta);
3174
3175
                    // Calculate what our "new" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator
3176
0
                    TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3177
0
                    if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
3178
0
                        v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
3179
0
                    float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3180
3181
0
                    if (delta > 0)
3182
0
                        g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta);
3183
0
                    else
3184
0
                        g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta);
3185
0
                }
3186
3187
0
                g.SliderCurrentAccumDirty = false;
3188
0
            }
3189
0
        }
3190
3191
0
        if (set_new_value)
3192
0
            if ((g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))
3193
0
                set_new_value = false;
3194
3195
0
        if (set_new_value)
3196
0
        {
3197
0
            TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3198
3199
            // Round to user desired precision based on format string
3200
0
            if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))
3201
0
                v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);
3202
3203
            // Apply result
3204
0
            if (*v != v_new)
3205
0
            {
3206
0
                *v = v_new;
3207
0
                value_changed = true;
3208
0
            }
3209
0
        }
3210
0
    }
3211
3212
0
    if (slider_sz < 1.0f)
3213
0
    {
3214
0
        *out_grab_bb = ImRect(bb.Min, bb.Min);
3215
0
    }
3216
0
    else
3217
0
    {
3218
        // Output grab position so it can be displayed by the caller
3219
0
        float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);
3220
0
        if (axis == ImGuiAxis_Y)
3221
0
            grab_t = 1.0f - grab_t;
3222
0
        const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);
3223
0
        if (axis == ImGuiAxis_X)
3224
0
            *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding);
3225
0
        else
3226
0
            *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f);
3227
0
    }
3228
3229
0
    return value_changed;
3230
0
}
Unexecuted instantiation: _ZN5ImGui15SliderBehaviorTIiifEEbRK6ImRectjiPT_S4_S4_PKciPS1_
Unexecuted instantiation: _ZN5ImGui15SliderBehaviorTIjifEEbRK6ImRectjiPT_S4_S4_PKciPS1_
Unexecuted instantiation: _ZN5ImGui15SliderBehaviorTIxxdEEbRK6ImRectjiPT_S4_S4_PKciPS1_
Unexecuted instantiation: _ZN5ImGui15SliderBehaviorTIyxdEEbRK6ImRectjiPT_S4_S4_PKciPS1_
Unexecuted instantiation: _ZN5ImGui15SliderBehaviorTIfffEEbRK6ImRectjiPT_S4_S4_PKciPS1_
Unexecuted instantiation: _ZN5ImGui15SliderBehaviorTIdddEEbRK6ImRectjiPT_S4_S4_PKciPS1_
3231
3232
// For 32-bit and larger types, slider bounds are limited to half the natural type range.
3233
// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.
3234
// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.
3235
bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)
3236
0
{
3237
    // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert.
3238
0
    IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flags! Has the legacy 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.");
3239
0
    IM_ASSERT((flags & ImGuiSliderFlags_WrapAround) == 0); // Not supported by SliderXXX(), only by DragXXX()
3240
3241
0
    switch (data_type)
3242
0
    {
3243
0
    case ImGuiDataType_S8:  { ImS32 v32 = (ImS32)*(ImS8*)p_v;  bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min,  *(const ImS8*)p_max,  format, flags, out_grab_bb); if (r) *(ImS8*)p_v  = (ImS8)v32;  return r; }
3244
0
    case ImGuiDataType_U8:  { ImU32 v32 = (ImU32)*(ImU8*)p_v;  bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min,  *(const ImU8*)p_max,  format, flags, out_grab_bb); if (r) *(ImU8*)p_v  = (ImU8)v32;  return r; }
3245
0
    case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }
3246
0
    case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }
3247
0
    case ImGuiDataType_S32:
3248
0
        IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2);
3249
0
        return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v,  *(const ImS32*)p_min,  *(const ImS32*)p_max,  format, flags, out_grab_bb);
3250
0
    case ImGuiDataType_U32:
3251
0
        IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2);
3252
0
        return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v,  *(const ImU32*)p_min,  *(const ImU32*)p_max,  format, flags, out_grab_bb);
3253
0
    case ImGuiDataType_S64:
3254
0
        IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2);
3255
0
        return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v,  *(const ImS64*)p_min,  *(const ImS64*)p_max,  format, flags, out_grab_bb);
3256
0
    case ImGuiDataType_U64:
3257
0
        IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2);
3258
0
        return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v,  *(const ImU64*)p_min,  *(const ImU64*)p_max,  format, flags, out_grab_bb);
3259
0
    case ImGuiDataType_Float:
3260
0
        IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f);
3261
0
        return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v,  *(const float*)p_min,  *(const float*)p_max,  format, flags, out_grab_bb);
3262
0
    case ImGuiDataType_Double:
3263
0
        IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f);
3264
0
        return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb);
3265
0
    case ImGuiDataType_COUNT: break;
3266
0
    }
3267
0
    IM_ASSERT(0);
3268
0
    return false;
3269
0
}
3270
3271
// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required.
3272
// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
3273
bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
3274
0
{
3275
0
    ImGuiWindow* window = GetCurrentWindow();
3276
0
    if (window->SkipItems)
3277
0
        return false;
3278
3279
0
    ImGuiContext& g = *GImGui;
3280
0
    const ImGuiStyle& style = g.Style;
3281
0
    const ImGuiID id = window->GetID(label);
3282
0
    const float w = CalcItemWidth();
3283
3284
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
3285
0
    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
3286
0
    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
3287
3288
0
    const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;
3289
0
    ItemSize(total_bb, style.FramePadding.y);
3290
0
    if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))
3291
0
        return false;
3292
3293
    // Default format string when passing NULL
3294
0
    if (format == NULL)
3295
0
        format = DataTypeGetInfo(data_type)->PrintFmt;
3296
3297
0
    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
3298
0
    bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);
3299
0
    if (!temp_input_is_active)
3300
0
    {
3301
        // Tabbing or CTRL+click on Slider turns it into an input box
3302
0
        const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
3303
0
        const bool make_active = (clicked || g.NavActivateId == id);
3304
0
        if (make_active && clicked)
3305
0
            SetKeyOwner(ImGuiKey_MouseLeft, id);
3306
0
        if (make_active && temp_input_allowed)
3307
0
            if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))
3308
0
                temp_input_is_active = true;
3309
3310
        // Store initial value (not used by main lib but available as a convenience but some mods e.g. to revert)
3311
0
        if (make_active)
3312
0
            memcpy(&g.ActiveIdValueOnActivation, p_data, DataTypeGetInfo(data_type)->Size);
3313
3314
0
        if (make_active && !temp_input_is_active)
3315
0
        {
3316
0
            SetActiveID(id, window);
3317
0
            SetFocusID(id, window);
3318
0
            FocusWindow(window);
3319
0
            g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
3320
0
        }
3321
0
    }
3322
3323
0
    if (temp_input_is_active)
3324
0
    {
3325
        // Only clamp CTRL+Click input when ImGuiSliderFlags_ClampOnInput is set (generally via ImGuiSliderFlags_AlwaysClamp)
3326
0
        const bool clamp_enabled = (flags & ImGuiSliderFlags_ClampOnInput) != 0;
3327
0
        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, clamp_enabled ? p_min : NULL, clamp_enabled ? p_max : NULL);
3328
0
    }
3329
3330
    // Draw frame
3331
0
    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
3332
0
    RenderNavCursor(frame_bb, id);
3333
0
    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
3334
3335
    // Slider behavior
3336
0
    ImRect grab_bb;
3337
0
    const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb);
3338
0
    if (value_changed)
3339
0
        MarkItemEdited(id);
3340
3341
    // Render grab
3342
0
    if (grab_bb.Max.x > grab_bb.Min.x)
3343
0
        window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
3344
3345
    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
3346
0
    char value_buf[64];
3347
0
    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
3348
0
    if (g.LogEnabled)
3349
0
        LogSetNextTextDecoration("{", "}");
3350
0
    RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));
3351
3352
0
    if (label_size.x > 0.0f)
3353
0
        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
3354
3355
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));
3356
0
    return value_changed;
3357
0
}
3358
3359
// Add multiple sliders on 1 line for compact edition of multiple components
3360
bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags)
3361
0
{
3362
0
    ImGuiWindow* window = GetCurrentWindow();
3363
0
    if (window->SkipItems)
3364
0
        return false;
3365
3366
0
    ImGuiContext& g = *GImGui;
3367
0
    bool value_changed = false;
3368
0
    BeginGroup();
3369
0
    PushID(label);
3370
0
    PushMultiItemsWidths(components, CalcItemWidth());
3371
0
    size_t type_size = GDataTypeInfo[data_type].Size;
3372
0
    for (int i = 0; i < components; i++)
3373
0
    {
3374
0
        PushID(i);
3375
0
        if (i > 0)
3376
0
            SameLine(0, g.Style.ItemInnerSpacing.x);
3377
0
        value_changed |= SliderScalar("", data_type, v, v_min, v_max, format, flags);
3378
0
        PopID();
3379
0
        PopItemWidth();
3380
0
        v = (void*)((char*)v + type_size);
3381
0
    }
3382
0
    PopID();
3383
3384
0
    const char* label_end = FindRenderedTextEnd(label);
3385
0
    if (label != label_end)
3386
0
    {
3387
0
        SameLine(0, g.Style.ItemInnerSpacing.x);
3388
0
        TextEx(label, label_end);
3389
0
    }
3390
3391
0
    EndGroup();
3392
0
    return value_changed;
3393
0
}
3394
3395
bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
3396
0
{
3397
0
    return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);
3398
0
}
3399
3400
bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
3401
0
{
3402
0
    return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags);
3403
0
}
3404
3405
bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
3406
0
{
3407
0
    return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags);
3408
0
}
3409
3410
bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
3411
0
{
3412
0
    return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags);
3413
0
}
3414
3415
bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags)
3416
0
{
3417
0
    if (format == NULL)
3418
0
        format = "%.0f deg";
3419
0
    float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);
3420
0
    bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);
3421
0
    if (value_changed)
3422
0
        *v_rad = v_deg * (2 * IM_PI) / 360.0f;
3423
0
    return value_changed;
3424
0
}
3425
3426
bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
3427
0
{
3428
0
    return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);
3429
0
}
3430
3431
bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
3432
0
{
3433
0
    return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags);
3434
0
}
3435
3436
bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
3437
0
{
3438
0
    return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags);
3439
0
}
3440
3441
bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
3442
0
{
3443
0
    return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags);
3444
0
}
3445
3446
bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)
3447
0
{
3448
0
    ImGuiWindow* window = GetCurrentWindow();
3449
0
    if (window->SkipItems)
3450
0
        return false;
3451
3452
0
    ImGuiContext& g = *GImGui;
3453
0
    const ImGuiStyle& style = g.Style;
3454
0
    const ImGuiID id = window->GetID(label);
3455
3456
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
3457
0
    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);
3458
0
    const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
3459
3460
0
    ItemSize(bb, style.FramePadding.y);
3461
0
    if (!ItemAdd(frame_bb, id))
3462
0
        return false;
3463
3464
    // Default format string when passing NULL
3465
0
    if (format == NULL)
3466
0
        format = DataTypeGetInfo(data_type)->PrintFmt;
3467
3468
0
    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags);
3469
0
    const bool clicked = hovered && IsMouseClicked(0, ImGuiInputFlags_None, id);
3470
0
    if (clicked || g.NavActivateId == id)
3471
0
    {
3472
0
        if (clicked)
3473
0
            SetKeyOwner(ImGuiKey_MouseLeft, id);
3474
0
        SetActiveID(id, window);
3475
0
        SetFocusID(id, window);
3476
0
        FocusWindow(window);
3477
0
        g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
3478
0
    }
3479
3480
    // Draw frame
3481
0
    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
3482
0
    RenderNavCursor(frame_bb, id);
3483
0
    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);
3484
3485
    // Slider behavior
3486
0
    ImRect grab_bb;
3487
0
    const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb);
3488
0
    if (value_changed)
3489
0
        MarkItemEdited(id);
3490
3491
    // Render grab
3492
0
    if (grab_bb.Max.y > grab_bb.Min.y)
3493
0
        window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);
3494
3495
    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.
3496
    // For the vertical slider we allow centered text to overlap the frame padding
3497
0
    char value_buf[64];
3498
0
    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);
3499
0
    RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f));
3500
0
    if (label_size.x > 0.0f)
3501
0
        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
3502
3503
0
    return value_changed;
3504
0
}
3505
3506
bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)
3507
0
{
3508
0
    return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);
3509
0
}
3510
3511
bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)
3512
0
{
3513
0
    return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);
3514
0
}
3515
3516
//-------------------------------------------------------------------------
3517
// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.
3518
//-------------------------------------------------------------------------
3519
// - ImParseFormatFindStart() [Internal]
3520
// - ImParseFormatFindEnd() [Internal]
3521
// - ImParseFormatTrimDecorations() [Internal]
3522
// - ImParseFormatSanitizeForPrinting() [Internal]
3523
// - ImParseFormatSanitizeForScanning() [Internal]
3524
// - ImParseFormatPrecision() [Internal]
3525
// - TempInputTextScalar() [Internal]
3526
// - InputScalar()
3527
// - InputScalarN()
3528
// - InputFloat()
3529
// - InputFloat2()
3530
// - InputFloat3()
3531
// - InputFloat4()
3532
// - InputInt()
3533
// - InputInt2()
3534
// - InputInt3()
3535
// - InputInt4()
3536
// - InputDouble()
3537
//-------------------------------------------------------------------------
3538
3539
// We don't use strchr() because our strings are usually very short and often start with '%'
3540
const char* ImParseFormatFindStart(const char* fmt)
3541
0
{
3542
0
    while (char c = fmt[0])
3543
0
    {
3544
0
        if (c == '%' && fmt[1] != '%')
3545
0
            return fmt;
3546
0
        else if (c == '%')
3547
0
            fmt++;
3548
0
        fmt++;
3549
0
    }
3550
0
    return fmt;
3551
0
}
3552
3553
const char* ImParseFormatFindEnd(const char* fmt)
3554
0
{
3555
    // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.
3556
0
    if (fmt[0] != '%')
3557
0
        return fmt;
3558
0
    const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));
3559
0
    const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));
3560
0
    for (char c; (c = *fmt) != 0; fmt++)
3561
0
    {
3562
0
        if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)
3563
0
            return fmt + 1;
3564
0
        if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)
3565
0
            return fmt + 1;
3566
0
    }
3567
0
    return fmt;
3568
0
}
3569
3570
// Extract the format out of a format string with leading or trailing decorations
3571
//  fmt = "blah blah"  -> return ""
3572
//  fmt = "%.3f"       -> return fmt
3573
//  fmt = "hello %.3f" -> return fmt + 6
3574
//  fmt = "%.3f hello" -> return buf written with "%.3f"
3575
const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size)
3576
0
{
3577
0
    const char* fmt_start = ImParseFormatFindStart(fmt);
3578
0
    if (fmt_start[0] != '%')
3579
0
        return "";
3580
0
    const char* fmt_end = ImParseFormatFindEnd(fmt_start);
3581
0
    if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data.
3582
0
        return fmt_start;
3583
0
    ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size));
3584
0
    return buf;
3585
0
}
3586
3587
// Sanitize format
3588
// - Zero terminate so extra characters after format (e.g. "%f123") don't confuse atof/atoi
3589
// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.
3590
void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size)
3591
0
{
3592
0
    const char* fmt_end = ImParseFormatFindEnd(fmt_in);
3593
0
    IM_UNUSED(fmt_out_size);
3594
0
    IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
3595
0
    while (fmt_in < fmt_end)
3596
0
    {
3597
0
        char c = *fmt_in++;
3598
0
        if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
3599
0
            *(fmt_out++) = c;
3600
0
    }
3601
0
    *fmt_out = 0; // Zero-terminate
3602
0
}
3603
3604
// - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d"
3605
const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size)
3606
0
{
3607
0
    const char* fmt_end = ImParseFormatFindEnd(fmt_in);
3608
0
    const char* fmt_out_begin = fmt_out;
3609
0
    IM_UNUSED(fmt_out_size);
3610
0
    IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!
3611
0
    bool has_type = false;
3612
0
    while (fmt_in < fmt_end)
3613
0
    {
3614
0
        char c = *fmt_in++;
3615
0
        if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#'))
3616
0
            continue;
3617
0
        has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits
3618
0
        if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.
3619
0
            *(fmt_out++) = c;
3620
0
    }
3621
0
    *fmt_out = 0; // Zero-terminate
3622
0
    return fmt_out_begin;
3623
0
}
3624
3625
template<typename TYPE>
3626
static const char* ImAtoi(const char* src, TYPE* output)
3627
0
{
3628
0
    int negative = 0;
3629
0
    if (*src == '-') { negative = 1; src++; }
3630
0
    if (*src == '+') { src++; }
3631
0
    TYPE v = 0;
3632
0
    while (*src >= '0' && *src <= '9')
3633
0
        v = (v * 10) + (*src++ - '0');
3634
0
    *output = negative ? -v : v;
3635
0
    return src;
3636
0
}
3637
3638
// Parse display precision back from the display format string
3639
// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.
3640
int ImParseFormatPrecision(const char* fmt, int default_precision)
3641
0
{
3642
0
    fmt = ImParseFormatFindStart(fmt);
3643
0
    if (fmt[0] != '%')
3644
0
        return default_precision;
3645
0
    fmt++;
3646
0
    while (*fmt >= '0' && *fmt <= '9')
3647
0
        fmt++;
3648
0
    int precision = INT_MAX;
3649
0
    if (*fmt == '.')
3650
0
    {
3651
0
        fmt = ImAtoi<int>(fmt + 1, &precision);
3652
0
        if (precision < 0 || precision > 99)
3653
0
            precision = default_precision;
3654
0
    }
3655
0
    if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation
3656
0
        precision = -1;
3657
0
    if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)
3658
0
        precision = -1;
3659
0
    return (precision == INT_MAX) ? default_precision : precision;
3660
0
}
3661
3662
// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets)
3663
// FIXME: Facilitate using this in variety of other situations.
3664
// FIXME: Among other things, setting ImGuiItemFlags_AllowDuplicateId in LastItemData is currently correct but
3665
// the expected relationship between TempInputXXX functions and LastItemData is a little fishy.
3666
bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags)
3667
0
{
3668
    // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id.
3669
    // We clear ActiveID on the first frame to allow the InputText() taking it back.
3670
0
    ImGuiContext& g = *GImGui;
3671
0
    const bool init = (g.TempInputId != id);
3672
0
    if (init)
3673
0
        ClearActiveID();
3674
3675
0
    g.CurrentWindow->DC.CursorPos = bb.Min;
3676
0
    g.LastItemData.ItemFlags |= ImGuiItemFlags_AllowDuplicateId;
3677
0
    bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);
3678
0
    if (init)
3679
0
    {
3680
        // First frame we started displaying the InputText widget, we expect it to take the active id.
3681
0
        IM_ASSERT(g.ActiveId == id);
3682
0
        g.TempInputId = g.ActiveId;
3683
0
    }
3684
0
    return value_changed;
3685
0
}
3686
3687
// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set!
3688
// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.
3689
// However this may not be ideal for all uses, as some user code may break on out of bound values.
3690
bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max)
3691
0
{
3692
    // FIXME: May need to clarify display behavior if format doesn't contain %.
3693
    // "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405
3694
0
    ImGuiContext& g = *GImGui;
3695
0
    const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);
3696
0
    char fmt_buf[32];
3697
0
    char data_buf[32];
3698
0
    format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));
3699
0
    if (format[0] == 0)
3700
0
        format = type_info->PrintFmt;
3701
0
    DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format);
3702
0
    ImStrTrimBlanks(data_buf);
3703
3704
0
    ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
3705
0
    g.LastItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited; // Because TempInputText() uses ImGuiInputTextFlags_MergedItem it doesn't submit a new item, so we poke LastItemData.
3706
0
    bool value_changed = false;
3707
0
    if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))
3708
0
    {
3709
        // Backup old value
3710
0
        size_t data_type_size = type_info->Size;
3711
0
        ImGuiDataTypeStorage data_backup;
3712
0
        memcpy(&data_backup, p_data, data_type_size);
3713
3714
        // Apply new value (or operations) then clamp
3715
0
        DataTypeApplyFromText(data_buf, data_type, p_data, format, NULL);
3716
0
        if (p_clamp_min || p_clamp_max)
3717
0
        {
3718
0
            if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)
3719
0
                ImSwap(p_clamp_min, p_clamp_max);
3720
0
            DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);
3721
0
        }
3722
3723
        // Only mark as edited if new value is different
3724
0
        g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
3725
0
        value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;
3726
0
        if (value_changed)
3727
0
            MarkItemEdited(id);
3728
0
    }
3729
0
    return value_changed;
3730
0
}
3731
3732
void ImGui::SetNextItemRefVal(ImGuiDataType data_type, void* p_data)
3733
0
{
3734
0
    ImGuiContext& g = *GImGui;
3735
0
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasRefVal;
3736
0
    memcpy(&g.NextItemData.RefVal, p_data, DataTypeGetInfo(data_type)->Size);
3737
0
}
3738
3739
// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional.
3740
// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.
3741
bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags)
3742
0
{
3743
0
    ImGuiWindow* window = GetCurrentWindow();
3744
0
    if (window->SkipItems)
3745
0
        return false;
3746
3747
0
    ImGuiContext& g = *GImGui;
3748
0
    ImGuiStyle& style = g.Style;
3749
0
    IM_ASSERT((flags & ImGuiInputTextFlags_EnterReturnsTrue) == 0); // Not supported by InputScalar(). Please open an issue if you this would be useful to you. Otherwise use IsItemDeactivatedAfterEdit()!
3750
3751
0
    if (format == NULL)
3752
0
        format = DataTypeGetInfo(data_type)->PrintFmt;
3753
3754
0
    void* p_data_default = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasRefVal) ? &g.NextItemData.RefVal : &g.DataTypeZeroValue;
3755
3756
0
    char buf[64];
3757
0
    if ((flags & ImGuiInputTextFlags_DisplayEmptyRefVal) && DataTypeCompare(data_type, p_data, p_data_default) == 0)
3758
0
        buf[0] = 0;
3759
0
    else
3760
0
        DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);
3761
3762
    // Disable the MarkItemEdited() call in InputText but keep ImGuiItemStatusFlags_Edited.
3763
    // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.
3764
0
    g.NextItemData.ItemFlags |= ImGuiItemFlags_NoMarkEdited;
3765
0
    flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint;
3766
3767
0
    bool value_changed = false;
3768
0
    if (p_step == NULL)
3769
0
    {
3770
0
        if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))
3771
0
            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);
3772
0
    }
3773
0
    else
3774
0
    {
3775
0
        const float button_size = GetFrameHeight();
3776
3777
0
        BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()
3778
0
        PushID(label);
3779
0
        SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
3780
0
        if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view
3781
0
            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format, (flags & ImGuiInputTextFlags_ParseEmptyRefVal) ? p_data_default : NULL);
3782
0
        IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
3783
3784
        // Step buttons
3785
0
        const ImVec2 backup_frame_padding = style.FramePadding;
3786
0
        style.FramePadding.x = style.FramePadding.y;
3787
0
        if (flags & ImGuiInputTextFlags_ReadOnly)
3788
0
            BeginDisabled();
3789
0
        PushItemFlag(ImGuiItemFlags_ButtonRepeat, true);
3790
0
        SameLine(0, style.ItemInnerSpacing.x);
3791
0
        if (ButtonEx("-", ImVec2(button_size, button_size)))
3792
0
        {
3793
0
            DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
3794
0
            value_changed = true;
3795
0
        }
3796
0
        SameLine(0, style.ItemInnerSpacing.x);
3797
0
        if (ButtonEx("+", ImVec2(button_size, button_size)))
3798
0
        {
3799
0
            DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);
3800
0
            value_changed = true;
3801
0
        }
3802
0
        PopItemFlag();
3803
0
        if (flags & ImGuiInputTextFlags_ReadOnly)
3804
0
            EndDisabled();
3805
3806
0
        const char* label_end = FindRenderedTextEnd(label);
3807
0
        if (label != label_end)
3808
0
        {
3809
0
            SameLine(0, style.ItemInnerSpacing.x);
3810
0
            TextEx(label, label_end);
3811
0
        }
3812
0
        style.FramePadding = backup_frame_padding;
3813
3814
0
        PopID();
3815
0
        EndGroup();
3816
0
    }
3817
3818
0
    g.LastItemData.ItemFlags &= ~ImGuiItemFlags_NoMarkEdited;
3819
0
    if (value_changed)
3820
0
        MarkItemEdited(g.LastItemData.ID);
3821
3822
0
    return value_changed;
3823
0
}
3824
3825
bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags)
3826
0
{
3827
0
    ImGuiWindow* window = GetCurrentWindow();
3828
0
    if (window->SkipItems)
3829
0
        return false;
3830
3831
0
    ImGuiContext& g = *GImGui;
3832
0
    bool value_changed = false;
3833
0
    BeginGroup();
3834
0
    PushID(label);
3835
0
    PushMultiItemsWidths(components, CalcItemWidth());
3836
0
    size_t type_size = GDataTypeInfo[data_type].Size;
3837
0
    for (int i = 0; i < components; i++)
3838
0
    {
3839
0
        PushID(i);
3840
0
        if (i > 0)
3841
0
            SameLine(0, g.Style.ItemInnerSpacing.x);
3842
0
        value_changed |= InputScalar("", data_type, p_data, p_step, p_step_fast, format, flags);
3843
0
        PopID();
3844
0
        PopItemWidth();
3845
0
        p_data = (void*)((char*)p_data + type_size);
3846
0
    }
3847
0
    PopID();
3848
3849
0
    const char* label_end = FindRenderedTextEnd(label);
3850
0
    if (label != label_end)
3851
0
    {
3852
0
        SameLine(0.0f, g.Style.ItemInnerSpacing.x);
3853
0
        TextEx(label, label_end);
3854
0
    }
3855
3856
0
    EndGroup();
3857
0
    return value_changed;
3858
0
}
3859
3860
bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)
3861
0
{
3862
0
    return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags);
3863
0
}
3864
3865
bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)
3866
0
{
3867
0
    return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);
3868
0
}
3869
3870
bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags)
3871
0
{
3872
0
    return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);
3873
0
}
3874
3875
bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags)
3876
0
{
3877
0
    return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);
3878
0
}
3879
3880
bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags)
3881
0
{
3882
    // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.
3883
0
    const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d";
3884
0
    return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags);
3885
0
}
3886
3887
bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)
3888
0
{
3889
0
    return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", flags);
3890
0
}
3891
3892
bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags)
3893
0
{
3894
0
    return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", flags);
3895
0
}
3896
3897
bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)
3898
0
{
3899
0
    return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", flags);
3900
0
}
3901
3902
bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)
3903
0
{
3904
0
    return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags);
3905
0
}
3906
3907
//-------------------------------------------------------------------------
3908
// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint
3909
//-------------------------------------------------------------------------
3910
// - imstb_textedit.h include
3911
// - InputText()
3912
// - InputTextWithHint()
3913
// - InputTextMultiline()
3914
// - InputTextEx() [Internal]
3915
// - DebugNodeInputTextState() [Internal]
3916
//-------------------------------------------------------------------------
3917
3918
namespace ImStb
3919
{
3920
#include "imstb_textedit.h"
3921
}
3922
3923
bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
3924
0
{
3925
0
    IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()
3926
0
    return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
3927
0
}
3928
3929
bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
3930
0
{
3931
0
    return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);
3932
0
}
3933
3934
bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
3935
0
{
3936
0
    IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() or  InputTextEx() manually if you need multi-line + hint.
3937
0
    return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);
3938
0
}
3939
3940
static ImVec2 InputTextCalcTextSize(ImGuiContext* ctx, const char* text_begin, const char* text_end_display, const char* text_end, const char** out_remaining, ImVec2* out_offset, ImDrawTextFlags flags)
3941
0
{
3942
0
    ImGuiContext& g = *ctx;
3943
0
    ImGuiInputTextState* obj = &g.InputTextState;
3944
0
    IM_ASSERT(text_end_display >= text_begin && text_end_display <= text_end);
3945
0
    return ImFontCalcTextSizeEx(g.Font, g.FontSize, FLT_MAX, obj->WrapWidth, text_begin, text_end_display, text_end, out_remaining, out_offset, flags);
3946
0
}
3947
3948
// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)
3949
// With our UTF-8 use of stb_textedit:
3950
// - STB_TEXTEDIT_GETCHAR is nothing more than a a "GETBYTE". It's only used to compare to ascii or to copy blocks of text so we are fine.
3951
// - One exception is the STB_TEXTEDIT_IS_SPACE feature which would expect a full char in order to handle full-width space such as 0x3000 (see ImCharIsBlankW).
3952
// - ...but we don't use that feature.
3953
namespace ImStb
3954
{
3955
0
static int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->TextLen; }
3956
0
static char    STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx >= 0 && idx <= obj->TextLen); return obj->TextSrc[idx]; }
3957
0
static float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { unsigned int c; ImTextCharFromUtf8(&c, obj->TextSrc + line_start_idx + char_idx, obj->TextSrc + obj->TextLen); if ((ImWchar)c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.FontBaked->GetCharAdvance((ImWchar)c) * g.FontBakedScale; }
3958
static char    STB_TEXTEDIT_NEWLINE = '\n';
3959
static void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
3960
0
{
3961
0
    const char* text = obj->TextSrc;
3962
0
    const char* text_remaining = NULL;
3963
0
    const ImVec2 size = InputTextCalcTextSize(obj->Ctx, text + line_start_idx, text + obj->TextLen, text + obj->TextLen, &text_remaining, NULL, ImDrawTextFlags_StopOnNewLine | ImDrawTextFlags_WrapKeepBlanks);
3964
0
    r->x0 = 0.0f;
3965
0
    r->x1 = size.x;
3966
0
    r->baseline_y_delta = size.y;
3967
0
    r->ymin = 0.0f;
3968
0
    r->ymax = size.y;
3969
0
    r->num_chars = (int)(text_remaining - (text + line_start_idx));
3970
0
}
3971
3972
0
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX  IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL
3973
0
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX  IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL
3974
3975
static int IMSTB_TEXTEDIT_GETNEXTCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
3976
0
{
3977
0
    if (idx >= obj->TextLen)
3978
0
        return obj->TextLen + 1;
3979
0
    unsigned int c;
3980
0
    return idx + ImTextCharFromUtf8(&c, obj->TextSrc + idx, obj->TextSrc + obj->TextLen);
3981
0
}
3982
3983
static int IMSTB_TEXTEDIT_GETPREVCHARINDEX_IMPL(ImGuiInputTextState* obj, int idx)
3984
0
{
3985
0
    if (idx <= 0)
3986
0
        return -1;
3987
0
    const char* p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, obj->TextSrc + idx);
3988
0
    return (int)(p - obj->TextSrc);
3989
0
}
3990
3991
static bool ImCharIsSeparatorW(unsigned int c)
3992
0
{
3993
0
    static const unsigned int separator_list[] =
3994
0
    {
3995
0
        ',', 0x3001, '.', 0x3002, ';', 0xFF1B, '(', 0xFF08, ')', 0xFF09, '{', 0xFF5B, '}', 0xFF5D,
3996
0
        '[', 0x300C, ']', 0x300D, '|', 0xFF5C, '!', 0xFF01, '\\', 0xFFE5, '/', 0x30FB, 0xFF0F,
3997
0
        '\n', '\r',
3998
0
    };
3999
0
    for (unsigned int separator : separator_list)
4000
0
        if (c == separator)
4001
0
            return true;
4002
0
    return false;
4003
0
}
4004
4005
static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)
4006
0
{
4007
    // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators.
4008
0
    if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
4009
0
        return 0;
4010
4011
0
    const char* curr_p = obj->TextSrc + idx;
4012
0
    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
4013
0
    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, curr_p, obj->TextSrc + obj->TextLen);
4014
0
    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, prev_p, obj->TextSrc + obj->TextLen);
4015
4016
0
    bool prev_white = ImCharIsBlankW(prev_c);
4017
0
    bool prev_separ = ImCharIsSeparatorW(prev_c);
4018
0
    bool curr_white = ImCharIsBlankW(curr_c);
4019
0
    bool curr_separ = ImCharIsSeparatorW(curr_c);
4020
0
    return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
4021
0
}
4022
static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)
4023
0
{
4024
0
    if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)
4025
0
        return 0;
4026
4027
0
    const char* curr_p = obj->TextSrc + idx;
4028
0
    const char* prev_p = ImTextFindPreviousUtf8Codepoint(obj->TextSrc, curr_p);
4029
0
    unsigned int prev_c; ImTextCharFromUtf8(&prev_c, curr_p, obj->TextSrc + obj->TextLen);
4030
0
    unsigned int curr_c; ImTextCharFromUtf8(&curr_c, prev_p, obj->TextSrc + obj->TextLen);
4031
4032
0
    bool prev_white = ImCharIsBlankW(prev_c);
4033
0
    bool prev_separ = ImCharIsSeparatorW(prev_c);
4034
0
    bool curr_white = ImCharIsBlankW(curr_c);
4035
0
    bool curr_separ = ImCharIsSeparatorW(curr_c);
4036
0
    return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);
4037
0
}
4038
static int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)
4039
0
{
4040
0
    idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);
4041
0
    while (idx >= 0 && !is_word_boundary_from_right(obj, idx))
4042
0
        idx = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, idx);
4043
0
    return idx < 0 ? 0 : idx;
4044
0
}
4045
static int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)
4046
0
{
4047
0
    int len = obj->TextLen;
4048
0
    idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
4049
0
    while (idx < len && !is_word_boundary_from_left(obj, idx))
4050
0
        idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
4051
0
    return idx > len ? len : idx;
4052
0
}
4053
static int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)
4054
0
{
4055
0
    idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
4056
0
    int len = obj->TextLen;
4057
0
    while (idx < len && !is_word_boundary_from_right(obj, idx))
4058
0
        idx = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, idx);
4059
0
    return idx > len ? len : idx;
4060
0
}
4061
0
static int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)  { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }
4062
0
#define STB_TEXTEDIT_MOVEWORDLEFT       STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h
4063
0
#define STB_TEXTEDIT_MOVEWORDRIGHT      STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
4064
4065
// Reimplementation of stb_textedit_move_line_start()/stb_textedit_move_line_end() which supports word-wrapping.
4066
static int STB_TEXTEDIT_MOVELINESTART_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)
4067
0
{
4068
0
    if (state->single_line)
4069
0
        return 0;
4070
4071
0
    if (obj->WrapWidth > 0.0f)
4072
0
    {
4073
0
        ImGuiContext& g = *obj->Ctx;
4074
0
        const char* p_cursor = obj->TextSrc + cursor;
4075
0
        const char* p_bol = ImStrbol(p_cursor, obj->TextSrc);
4076
0
        const char* p = p_bol;
4077
0
        const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough
4078
0
        while (p >= p_bol)
4079
0
        {
4080
0
            const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);
4081
0
            if (p == p_cursor) // If we are already on a visible beginning-of-line, return real beginning-of-line (would be same as regular handler below)
4082
0
                return (int)(p_bol - obj->TextSrc);
4083
0
            if (p_eol == p_cursor && obj->TextA[cursor] != '\n' && obj->LastMoveDirectionLR == ImGuiDir_Left)
4084
0
                return (int)(p_bol - obj->TextSrc);
4085
0
            if (p_eol >= p_cursor)
4086
0
                return (int)(p - obj->TextSrc);
4087
0
            p = (*p_eol == '\n') ? p_eol + 1 : p_eol;
4088
0
        }
4089
0
    }
4090
4091
    // Regular handler, same as stb_textedit_move_line_start()
4092
0
    while (cursor > 0)
4093
0
    {
4094
0
        int prev_cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(obj, cursor);
4095
0
        if (STB_TEXTEDIT_GETCHAR(obj, prev_cursor) == STB_TEXTEDIT_NEWLINE)
4096
0
            break;
4097
0
        cursor = prev_cursor;
4098
0
    }
4099
0
    return cursor;
4100
0
}
4101
4102
static int STB_TEXTEDIT_MOVELINEEND_IMPL(ImGuiInputTextState* obj, ImStb::STB_TexteditState* state, int cursor)
4103
0
{
4104
0
    int n = STB_TEXTEDIT_STRINGLEN(obj);
4105
0
    if (state->single_line)
4106
0
        return n;
4107
4108
0
    if (obj->WrapWidth > 0.0f)
4109
0
    {
4110
0
        ImGuiContext& g = *obj->Ctx;
4111
0
        const char* p_cursor = obj->TextSrc + cursor;
4112
0
        const char* p = ImStrbol(p_cursor, obj->TextSrc);
4113
0
        const char* text_end = obj->TextSrc + obj->TextLen; // End of line would be enough
4114
0
        while (p < text_end)
4115
0
        {
4116
0
            const char* p_eol = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, p, text_end, obj->WrapWidth, ImDrawTextFlags_WrapKeepBlanks);
4117
0
            cursor = (int)(p_eol - obj->TextSrc);
4118
0
            if (p_eol == p_cursor && obj->LastMoveDirectionLR != ImGuiDir_Left) // If we are already on a visible end-of-line, switch to regular handle
4119
0
                break;
4120
0
            if (p_eol > p_cursor)
4121
0
                return cursor;
4122
0
            p = (*p_eol == '\n') ? p_eol + 1 : p_eol;
4123
0
        }
4124
0
    }
4125
    // Regular handler, same as stb_textedit_move_line_end()
4126
0
    while (cursor < n && STB_TEXTEDIT_GETCHAR(obj, cursor) != STB_TEXTEDIT_NEWLINE)
4127
0
        cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(obj, cursor);
4128
0
    return cursor;
4129
0
}
4130
4131
0
#define STB_TEXTEDIT_MOVELINESTART      STB_TEXTEDIT_MOVELINESTART_IMPL
4132
0
#define STB_TEXTEDIT_MOVELINEEND        STB_TEXTEDIT_MOVELINEEND_IMPL
4133
4134
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
4135
0
{
4136
    // Offset remaining text (+ copy zero terminator)
4137
0
    IM_ASSERT(obj->TextSrc == obj->TextA.Data);
4138
0
    char* dst = obj->TextA.Data + pos;
4139
0
    char* src = obj->TextA.Data + pos + n;
4140
0
    memmove(dst, src, obj->TextLen - n - pos + 1);
4141
0
    obj->Edited = true;
4142
0
    obj->TextLen -= n;
4143
0
}
4144
4145
static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const char* new_text, int new_text_len)
4146
0
{
4147
0
    const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
4148
0
    const int text_len = obj->TextLen;
4149
0
    IM_ASSERT(pos <= text_len);
4150
4151
0
    if (!is_resizable && (new_text_len + obj->TextLen + 1 > obj->BufCapacity))
4152
0
        return false;
4153
4154
    // Grow internal buffer if needed
4155
0
    IM_ASSERT(obj->TextSrc == obj->TextA.Data);
4156
0
    if (new_text_len + text_len + 1 > obj->TextA.Size)
4157
0
    {
4158
0
        if (!is_resizable)
4159
0
            return false;
4160
0
        obj->TextA.resize(text_len + ImClamp(new_text_len, 32, ImMax(256, new_text_len)) + 1);
4161
0
        obj->TextSrc = obj->TextA.Data;
4162
0
    }
4163
4164
0
    char* text = obj->TextA.Data;
4165
0
    if (pos != text_len)
4166
0
        memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos));
4167
0
    memcpy(text + pos, new_text, (size_t)new_text_len);
4168
4169
0
    obj->Edited = true;
4170
0
    obj->TextLen += new_text_len;
4171
0
    obj->TextA[obj->TextLen] = '\0';
4172
4173
0
    return true;
4174
0
}
4175
4176
// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)
4177
0
#define STB_TEXTEDIT_K_LEFT         0x200000 // keyboard input to move cursor left
4178
0
#define STB_TEXTEDIT_K_RIGHT        0x200001 // keyboard input to move cursor right
4179
0
#define STB_TEXTEDIT_K_UP           0x200002 // keyboard input to move cursor up
4180
0
#define STB_TEXTEDIT_K_DOWN         0x200003 // keyboard input to move cursor down
4181
0
#define STB_TEXTEDIT_K_LINESTART    0x200004 // keyboard input to move cursor to start of line
4182
0
#define STB_TEXTEDIT_K_LINEEND      0x200005 // keyboard input to move cursor to end of line
4183
0
#define STB_TEXTEDIT_K_TEXTSTART    0x200006 // keyboard input to move cursor to start of text
4184
0
#define STB_TEXTEDIT_K_TEXTEND      0x200007 // keyboard input to move cursor to end of text
4185
0
#define STB_TEXTEDIT_K_DELETE       0x200008 // keyboard input to delete selection or character under cursor
4186
0
#define STB_TEXTEDIT_K_BACKSPACE    0x200009 // keyboard input to delete selection or character left of cursor
4187
0
#define STB_TEXTEDIT_K_UNDO         0x20000A // keyboard input to perform undo
4188
0
#define STB_TEXTEDIT_K_REDO         0x20000B // keyboard input to perform redo
4189
0
#define STB_TEXTEDIT_K_WORDLEFT     0x20000C // keyboard input to move cursor left one word
4190
0
#define STB_TEXTEDIT_K_WORDRIGHT    0x20000D // keyboard input to move cursor right one word
4191
0
#define STB_TEXTEDIT_K_PGUP         0x20000E // keyboard input to move cursor up a page
4192
0
#define STB_TEXTEDIT_K_PGDOWN       0x20000F // keyboard input to move cursor down a page
4193
0
#define STB_TEXTEDIT_K_SHIFT        0x400000
4194
4195
#define IMSTB_TEXTEDIT_IMPLEMENTATION
4196
0
#define IMSTB_TEXTEDIT_memmove memmove
4197
#include "imstb_textedit.h"
4198
4199
// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling
4200
// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
4201
static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
4202
0
{
4203
0
    stb_text_makeundo_replace(str, state, 0, str->TextLen, text_len);
4204
0
    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->TextLen);
4205
0
    state->cursor = state->select_start = state->select_end = 0;
4206
0
    if (text_len <= 0)
4207
0
        return;
4208
0
    if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len))
4209
0
    {
4210
0
        state->cursor = state->select_start = state->select_end = text_len;
4211
0
        state->has_preferred_x = 0;
4212
0
        return;
4213
0
    }
4214
0
    IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace()
4215
0
}
4216
4217
} // namespace ImStb
4218
4219
// We added an extra indirection where 'Stb' is heap-allocated, in order facilitate the work of bindings generators.
4220
ImGuiInputTextState::ImGuiInputTextState()
4221
1
{
4222
1
    memset(this, 0, sizeof(*this));
4223
1
    Stb = IM_NEW(ImStbTexteditState);
4224
1
    memset(Stb, 0, sizeof(*Stb));
4225
1
}
4226
4227
ImGuiInputTextState::~ImGuiInputTextState()
4228
1
{
4229
1
    IM_DELETE(Stb);
4230
1
}
4231
4232
void ImGuiInputTextState::OnKeyPressed(int key)
4233
0
{
4234
0
    stb_textedit_key(this, Stb, key);
4235
0
    CursorFollow = true;
4236
0
    CursorAnimReset();
4237
0
    const int key_u = (key & ~STB_TEXTEDIT_K_SHIFT);
4238
0
    if (key_u == STB_TEXTEDIT_K_LEFT || key_u == STB_TEXTEDIT_K_LINESTART || key_u == STB_TEXTEDIT_K_TEXTSTART || key_u == STB_TEXTEDIT_K_BACKSPACE || key_u == STB_TEXTEDIT_K_WORDLEFT)
4239
0
        LastMoveDirectionLR = ImGuiDir_Left;
4240
0
    else if (key_u == STB_TEXTEDIT_K_RIGHT || key_u == STB_TEXTEDIT_K_LINEEND || key_u == STB_TEXTEDIT_K_TEXTEND || key_u == STB_TEXTEDIT_K_DELETE || key_u == STB_TEXTEDIT_K_WORDRIGHT)
4241
0
        LastMoveDirectionLR = ImGuiDir_Right;
4242
0
}
4243
4244
void ImGuiInputTextState::OnCharPressed(unsigned int c)
4245
0
{
4246
    // Convert the key to a UTF8 byte sequence.
4247
    // The changes we had to make to stb_textedit_key made it very much UTF-8 specific which is not too great.
4248
0
    char utf8[5];
4249
0
    ImTextCharToUtf8(utf8, c);
4250
0
    stb_textedit_text(this, Stb, utf8, (int)ImStrlen(utf8));
4251
0
    CursorFollow = true;
4252
0
    CursorAnimReset();
4253
0
}
4254
4255
// Those functions are not inlined in imgui_internal.h, allowing us to hide ImStbTexteditState from that header.
4256
0
void ImGuiInputTextState::CursorAnimReset()                 { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking
4257
0
void ImGuiInputTextState::CursorClamp()                     { Stb->cursor = ImMin(Stb->cursor, TextLen); Stb->select_start = ImMin(Stb->select_start, TextLen); Stb->select_end = ImMin(Stb->select_end, TextLen); }
4258
0
bool ImGuiInputTextState::HasSelection() const              { return Stb->select_start != Stb->select_end; }
4259
0
void ImGuiInputTextState::ClearSelection()                  { Stb->select_start = Stb->select_end = Stb->cursor; }
4260
0
int  ImGuiInputTextState::GetCursorPos() const              { return Stb->cursor; }
4261
0
int  ImGuiInputTextState::GetSelectionStart() const         { return Stb->select_start; }
4262
0
int  ImGuiInputTextState::GetSelectionEnd() const           { return Stb->select_end; }
4263
0
float ImGuiInputTextState::GetPreferredOffsetX() const      { return Stb->has_preferred_x ? Stb->preferred_x : -1; }
4264
0
void ImGuiInputTextState::SelectAll()                       { Stb->select_start = 0; Stb->cursor = Stb->select_end = TextLen; Stb->has_preferred_x = 0; }
4265
0
void ImGuiInputTextState::ReloadUserBufAndSelectAll()       { WantReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; }
4266
0
void ImGuiInputTextState::ReloadUserBufAndKeepSelection()   { WantReloadUserBuf = true; ReloadSelectionStart = Stb->select_start; ReloadSelectionEnd = Stb->select_end; }
4267
0
void ImGuiInputTextState::ReloadUserBufAndMoveToEnd()       { WantReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; }
4268
4269
ImGuiInputTextCallbackData::ImGuiInputTextCallbackData()
4270
0
{
4271
0
    memset(this, 0, sizeof(*this));
4272
0
}
4273
4274
// Public API to manipulate UTF-8 text from within a callback.
4275
// FIXME: The existence of this rarely exercised code path is a bit of a nuisance.
4276
// Historically they existed because STB_TEXTEDIT_INSERTCHARS() etc. worked on our ImWchar
4277
// buffer, but nowadays they both work on UTF-8 data. Should aim to merge both.
4278
void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)
4279
0
{
4280
0
    IM_ASSERT(pos + bytes_count <= BufTextLen);
4281
0
    char* dst = Buf + pos;
4282
0
    const char* src = Buf + pos + bytes_count;
4283
0
    memmove(dst, src, BufTextLen - bytes_count - pos + 1);
4284
4285
0
    if (CursorPos >= pos + bytes_count)
4286
0
        CursorPos -= bytes_count;
4287
0
    else if (CursorPos >= pos)
4288
0
        CursorPos = pos;
4289
0
    SelectionStart = SelectionEnd = CursorPos;
4290
0
    BufDirty = true;
4291
0
    BufTextLen -= bytes_count;
4292
0
}
4293
4294
void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)
4295
0
{
4296
    // Accept null ranges
4297
0
    if (new_text == new_text_end)
4298
0
        return;
4299
4300
0
    ImGuiContext& g = *Ctx;
4301
0
    ImGuiInputTextState* obj = &g.InputTextState;
4302
0
    IM_ASSERT(obj->ID != 0 && g.ActiveId == obj->ID);
4303
4304
    // Grow internal buffer if needed
4305
0
    const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;
4306
0
    const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)ImStrlen(new_text);
4307
0
    if (new_text_len + BufTextLen + 1 > obj->TextA.Size && (Flags & ImGuiInputTextFlags_ReadOnly) == 0)
4308
0
    {
4309
0
        if (!is_resizable)
4310
0
            return;
4311
4312
0
        IM_ASSERT(Buf == obj->TextA.Data);
4313
0
        int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;
4314
0
        obj->TextA.resize(new_buf_size + 1);
4315
0
        obj->TextSrc = obj->TextA.Data;
4316
0
        Buf = obj->TextA.Data;
4317
0
        BufSize = obj->BufCapacity = new_buf_size;
4318
0
    }
4319
4320
0
    if (BufTextLen != pos)
4321
0
        memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));
4322
0
    memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));
4323
0
    Buf[BufTextLen + new_text_len] = '\0';
4324
4325
0
    BufDirty = true;
4326
0
    BufTextLen += new_text_len;
4327
0
    if (CursorPos >= pos)
4328
0
        CursorPos += new_text_len;
4329
0
    CursorPos = ImClamp(CursorPos, 0, BufTextLen);
4330
0
    SelectionStart = SelectionEnd = CursorPos;
4331
0
}
4332
4333
void ImGui::PushPasswordFont()
4334
0
{
4335
0
    ImGuiContext& g = *GImGui;
4336
0
    ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
4337
0
    IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
4338
0
    ImFontGlyph* glyph = g.FontBaked->FindGlyph('*');
4339
0
    g.InputTextPasswordFontBackupFlags = g.Font->Flags;
4340
0
    backup->FallbackGlyphIndex = g.FontBaked->FallbackGlyphIndex;
4341
0
    backup->FallbackAdvanceX = g.FontBaked->FallbackAdvanceX;
4342
0
    backup->IndexLookup.swap(g.FontBaked->IndexLookup);
4343
0
    backup->IndexAdvanceX.swap(g.FontBaked->IndexAdvanceX);
4344
0
    g.Font->Flags |= ImFontFlags_NoLoadGlyphs;
4345
0
    g.FontBaked->FallbackGlyphIndex = g.FontBaked->Glyphs.index_from_ptr(glyph);
4346
0
    g.FontBaked->FallbackAdvanceX = glyph->AdvanceX;
4347
0
}
4348
4349
void ImGui::PopPasswordFont()
4350
0
{
4351
0
    ImGuiContext& g = *GImGui;
4352
0
    ImFontBaked* backup = &g.InputTextPasswordFontBackupBaked;
4353
0
    g.Font->Flags = g.InputTextPasswordFontBackupFlags;
4354
0
    g.FontBaked->FallbackGlyphIndex = backup->FallbackGlyphIndex;
4355
0
    g.FontBaked->FallbackAdvanceX = backup->FallbackAdvanceX;
4356
0
    g.FontBaked->IndexLookup.swap(backup->IndexLookup);
4357
0
    g.FontBaked->IndexAdvanceX.swap(backup->IndexAdvanceX);
4358
0
    IM_ASSERT(backup->IndexAdvanceX.Size == 0 && backup->IndexLookup.Size == 0);
4359
0
}
4360
4361
// Return false to discard a character.
4362
static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard)
4363
0
{
4364
0
    unsigned int c = *p_char;
4365
4366
    // Filter non-printable (NB: isprint is unreliable! see #2467)
4367
0
    bool apply_named_filters = true;
4368
0
    if (c < 0x20)
4369
0
    {
4370
0
        bool pass = false;
4371
0
        pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0;    // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code)
4372
0
        if (c == '\n' && input_source_is_clipboard && (flags & ImGuiInputTextFlags_Multiline) == 0) // In single line mode, replace \n with a space
4373
0
        {
4374
0
            c = *p_char = ' ';
4375
0
            pass = true;
4376
0
        }
4377
0
        pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0;
4378
0
        pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0;
4379
0
        if (!pass)
4380
0
            return false;
4381
0
        apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted.
4382
0
    }
4383
4384
0
    if (input_source_is_clipboard == false)
4385
0
    {
4386
        // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)
4387
0
        if (c == 127)
4388
0
            return false;
4389
4390
        // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME)
4391
0
        if (c >= 0xE000 && c <= 0xF8FF)
4392
0
            return false;
4393
0
    }
4394
4395
    // Filter Unicode ranges we are not handling in this build
4396
0
    if (c > IM_UNICODE_CODEPOINT_MAX)
4397
0
        return false;
4398
4399
    // Generic named filters
4400
0
    if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint)))
4401
0
    {
4402
        // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, "de_DE.UTF-8");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'.
4403
        // The standard mandate that programs starts in the "C" locale where the decimal point is '.'.
4404
        // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point.
4405
        // Change the default decimal_point with:
4406
        //   ImGui::GetPlatformIO()->Platform_LocaleDecimalPoint = *localeconv()->decimal_point;
4407
        // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions.
4408
0
        ImGuiContext& g = *ctx;
4409
0
        const unsigned c_decimal_point = (unsigned int)g.PlatformIO.Platform_LocaleDecimalPoint;
4410
0
        if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | (ImGuiInputTextFlags)ImGuiInputTextFlags_LocalizeDecimalPoint))
4411
0
            if (c == '.' || c == ',')
4412
0
                c = c_decimal_point;
4413
4414
        // Full-width -> half-width conversion for numeric fields: https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)
4415
        // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may
4416
        // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font.
4417
0
        if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal))
4418
0
            if (c >= 0xFF01 && c <= 0xFF5E)
4419
0
                c = c - 0xFF01 + 0x21;
4420
4421
        // Allow 0-9 . - + * /
4422
0
        if (flags & ImGuiInputTextFlags_CharsDecimal)
4423
0
            if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/'))
4424
0
                return false;
4425
4426
        // Allow 0-9 . - + * / e E
4427
0
        if (flags & ImGuiInputTextFlags_CharsScientific)
4428
0
            if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))
4429
0
                return false;
4430
4431
        // Allow 0-9 a-F A-F
4432
0
        if (flags & ImGuiInputTextFlags_CharsHexadecimal)
4433
0
            if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))
4434
0
                return false;
4435
4436
        // Turn a-z into A-Z
4437
0
        if (flags & ImGuiInputTextFlags_CharsUppercase)
4438
0
            if (c >= 'a' && c <= 'z')
4439
0
                c += (unsigned int)('A' - 'a');
4440
4441
0
        if (flags & ImGuiInputTextFlags_CharsNoBlank)
4442
0
            if (ImCharIsBlankW(c))
4443
0
                return false;
4444
4445
0
        *p_char = c;
4446
0
    }
4447
4448
    // Custom callback filter
4449
0
    if (flags & ImGuiInputTextFlags_CallbackCharFilter)
4450
0
    {
4451
0
        ImGuiContext& g = *GImGui;
4452
0
        ImGuiInputTextCallbackData callback_data;
4453
0
        callback_data.Ctx = &g;
4454
0
        callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;
4455
0
        callback_data.EventChar = (ImWchar)c;
4456
0
        callback_data.Flags = flags;
4457
0
        callback_data.UserData = user_data;
4458
0
        if (callback(&callback_data) != 0)
4459
0
            return false;
4460
0
        *p_char = callback_data.EventChar;
4461
0
        if (!callback_data.EventChar)
4462
0
            return false;
4463
0
    }
4464
4465
0
    return true;
4466
0
}
4467
4468
// Find the shortest single replacement we can make to get from old_buf to new_buf
4469
// Note that this doesn't directly alter state->TextA, state->TextLen. They are expected to be made valid separately.
4470
// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.
4471
static void InputTextReconcileUndoState(ImGuiInputTextState* state, const char* old_buf, int old_length, const char* new_buf, int new_length)
4472
0
{
4473
0
    const int shorter_length = ImMin(old_length, new_length);
4474
0
    int first_diff;
4475
0
    for (first_diff = 0; first_diff < shorter_length; first_diff++)
4476
0
        if (old_buf[first_diff] != new_buf[first_diff])
4477
0
            break;
4478
0
    if (first_diff == old_length && first_diff == new_length)
4479
0
        return;
4480
4481
0
    int old_last_diff = old_length   - 1;
4482
0
    int new_last_diff = new_length - 1;
4483
0
    for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)
4484
0
        if (old_buf[old_last_diff] != new_buf[new_last_diff])
4485
0
            break;
4486
4487
0
    const int insert_len = new_last_diff - first_diff + 1;
4488
0
    const int delete_len = old_last_diff - first_diff + 1;
4489
0
    if (insert_len > 0 || delete_len > 0)
4490
0
        if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb->undostate, first_diff, delete_len, insert_len))
4491
0
            for (int i = 0; i < delete_len; i++)
4492
0
                p[i] = old_buf[first_diff + i];
4493
0
}
4494
4495
// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)
4496
// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714)
4497
// It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick,
4498
// but that more likely be attractive when we do have _NoLiveEdit flag available.
4499
void ImGui::InputTextDeactivateHook(ImGuiID id)
4500
0
{
4501
0
    ImGuiContext& g = *GImGui;
4502
0
    ImGuiInputTextState* state = &g.InputTextState;
4503
0
    if (id == 0 || state->ID != id)
4504
0
        return;
4505
0
    g.InputTextDeactivatedState.ID = state->ID;
4506
0
    if (state->Flags & ImGuiInputTextFlags_ReadOnly)
4507
0
    {
4508
0
        g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat.
4509
0
    }
4510
0
    else
4511
0
    {
4512
0
        IM_ASSERT(state->TextA.Data != 0);
4513
0
        IM_ASSERT(state->TextA[state->TextLen] == 0);
4514
0
        g.InputTextDeactivatedState.TextA.resize(state->TextLen + 1);
4515
0
        memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->TextLen + 1);
4516
0
    }
4517
0
}
4518
4519
static int* ImLowerBound(int* in_begin, int* in_end, int v)
4520
0
{
4521
0
    int* in_p = in_begin;
4522
0
    for (size_t count = (size_t)(in_end - in_p); count > 0; )
4523
0
    {
4524
0
        size_t count2 = count >> 1;
4525
0
        int* mid = in_p + count2;
4526
0
        if (*mid < v)
4527
0
        {
4528
0
            in_p = ++mid;
4529
0
            count -= count2 + 1;
4530
0
        }
4531
0
        else
4532
0
        {
4533
0
            count = count2;
4534
0
        }
4535
0
    }
4536
0
    return in_p;
4537
0
}
4538
4539
// FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool?
4540
// 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive)
4541
static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end)
4542
0
{
4543
0
    ImGuiContext& g = *GImGui;
4544
0
    int size = 0;
4545
0
    const char* s;
4546
0
    if (flags & ImGuiInputTextFlags_WordWrap)
4547
0
    {
4548
0
        for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s)
4549
0
        {
4550
0
            if (size++ <= max_output_buffer_size)
4551
0
                line_index->Offsets.push_back((int)(s - buf));
4552
0
            s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks);
4553
0
        }
4554
0
    }
4555
0
    else if (buf_end != NULL)
4556
0
    {
4557
0
        for (s = buf; s < buf_end; s = s ? s + 1 : buf_end)
4558
0
        {
4559
0
            if (size++ <= max_output_buffer_size)
4560
0
                line_index->Offsets.push_back((int)(s - buf));
4561
0
            s = (const char*)ImMemchr(s, '\n', buf_end - s);
4562
0
        }
4563
0
    }
4564
0
    else
4565
0
    {
4566
0
        const char* s_eol;
4567
0
        for (s = buf; ; s = s_eol + 1)
4568
0
        {
4569
0
            if (size++ <= max_output_buffer_size)
4570
0
                line_index->Offsets.push_back((int)(s - buf));
4571
0
            if ((s_eol = strchr(s, '\n')) != NULL)
4572
0
                continue;
4573
0
            s += strlen(s);
4574
0
            break;
4575
0
        }
4576
0
    }
4577
0
    if (out_buf_end != NULL)
4578
0
        *out_buf_end = buf_end = s;
4579
0
    if (size == 0)
4580
0
    {
4581
0
        line_index->Offsets.push_back(0);
4582
0
        size++;
4583
0
    }
4584
0
    if (buf_end > buf && buf_end[-1] == '\n' && size <= max_output_buffer_size)
4585
0
    {
4586
0
        line_index->Offsets.push_back((int)(buf_end - buf));
4587
0
        size++;
4588
0
    }
4589
0
    return size;
4590
0
}
4591
4592
static ImVec2 InputTextLineIndexGetPosOffset(ImGuiContext& g, ImGuiInputTextState* state, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, int cursor_n)
4593
0
{
4594
0
    const char* cursor_ptr = buf + cursor_n;
4595
0
    int* it_begin = line_index->Offsets.begin();
4596
0
    int* it_end = line_index->Offsets.end();
4597
0
    const int* it = ImLowerBound(it_begin, it_end, cursor_n);
4598
0
    if (it > it_begin)
4599
0
        if (it == it_end || *it != cursor_n || (state != NULL && state->WrapWidth > 0.0f && state->LastMoveDirectionLR == ImGuiDir_Right && cursor_ptr[-1] != '\n' && cursor_ptr[-1] != 0))
4600
0
            it--;
4601
4602
0
    const int line_no = (it == it_begin) ? 0 : line_index->Offsets.index_from_ptr(it);
4603
0
    const char* line_start = line_index->get_line_begin(buf, line_no);
4604
0
    ImVec2 offset;
4605
0
    offset.x = InputTextCalcTextSize(&g, line_start, cursor_ptr, buf_end, NULL, NULL, ImDrawTextFlags_WrapKeepBlanks).x;
4606
0
    offset.y = (line_no + 1) * g.FontSize;
4607
0
    return offset;
4608
0
}
4609
4610
// Edit a string of text
4611
// - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!".
4612
//   This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match
4613
//   Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.
4614
// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.
4615
// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h
4616
// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are
4617
//  doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188)
4618
bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)
4619
0
{
4620
0
    ImGuiWindow* window = GetCurrentWindow();
4621
0
    if (window->SkipItems)
4622
0
        return false;
4623
4624
0
    IM_ASSERT(buf != NULL && buf_size >= 0);
4625
0
    IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));        // Can't use both together (they both use up/down keys)
4626
0
    IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)
4627
0
    IM_ASSERT(!((flags & ImGuiInputTextFlags_ElideLeft) && (flags & ImGuiInputTextFlags_Multiline)));              // Multiline does not not work with left-trimming
4628
0
    IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Password) == 0);         // WordWrap does not work with Password mode.
4629
0
    IM_ASSERT((flags & ImGuiInputTextFlags_WordWrap) == 0 || (flags & ImGuiInputTextFlags_Multiline) != 0);        // WordWrap does not work in single-line mode.
4630
4631
0
    ImGuiContext& g = *GImGui;
4632
0
    ImGuiIO& io = g.IO;
4633
0
    const ImGuiStyle& style = g.Style;
4634
4635
0
    const bool RENDER_SELECTION_WHEN_INACTIVE = false;
4636
0
    const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;
4637
4638
0
    if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)
4639
0
        BeginGroup();
4640
0
    const ImGuiID id = window->GetID(label);
4641
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
4642
0
    const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line
4643
0
    const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);
4644
4645
0
    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
4646
0
    const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size);
4647
4648
0
    ImGuiWindow* draw_window = window;
4649
0
    ImVec2 inner_size = frame_size;
4650
0
    ImGuiLastItemData item_data_backup;
4651
0
    if (is_multiline)
4652
0
    {
4653
0
        ImVec2 backup_pos = window->DC.CursorPos;
4654
0
        ItemSize(total_bb, style.FramePadding.y);
4655
0
        if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
4656
0
        {
4657
0
            EndGroup();
4658
0
            return false;
4659
0
        }
4660
0
        item_data_backup = g.LastItemData;
4661
0
        window->DC.CursorPos = backup_pos;
4662
4663
        // Prevent NavActivation from explicit Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping.
4664
0
        if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && !(g.NavActivateFlags & ImGuiActivateFlags_FromFocusApi) && (flags & ImGuiInputTextFlags_AllowTabInput))
4665
0
            g.NavActivateId = 0;
4666
4667
        // Prevent NavActivate reactivating in BeginChild() when we are already active.
4668
0
        const ImGuiID backup_activate_id = g.NavActivateId;
4669
0
        if (g.ActiveId == id) // Prevent reactivation
4670
0
            g.NavActivateId = 0;
4671
4672
        // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.
4673
0
        PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
4674
0
        PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);
4675
0
        PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
4676
0
        PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges
4677
0
        bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoMove);
4678
0
        g.NavActivateId = backup_activate_id;
4679
0
        PopStyleVar(3);
4680
0
        PopStyleColor();
4681
0
        if (!child_visible)
4682
0
        {
4683
0
            EndChild();
4684
0
            EndGroup();
4685
0
            return false;
4686
0
        }
4687
0
        draw_window = g.CurrentWindow; // Child window
4688
0
        draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can "enter" into it.
4689
0
        draw_window->DC.CursorPos += style.FramePadding;
4690
0
        inner_size.x -= draw_window->ScrollbarSizes.x;
4691
0
    }
4692
0
    else
4693
0
    {
4694
        // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd)
4695
0
        ItemSize(total_bb, style.FramePadding.y);
4696
0
        if (!(flags & ImGuiInputTextFlags_MergedItem))
4697
0
            if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))
4698
0
                return false;
4699
0
    }
4700
4701
    // Ensure mouse cursor is set even after switching to keyboard/gamepad mode. May generalize further? (#6417)
4702
0
    bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.ItemFlags | ImGuiItemFlags_NoNavDisableMouseHover);
4703
0
    if (hovered)
4704
0
        SetMouseCursor(ImGuiMouseCursor_TextInput);
4705
0
    if (hovered && g.NavHighlightItemUnderNav)
4706
0
        hovered = false;
4707
4708
    // We are only allowed to access the state if we are already the active widget.
4709
0
    ImGuiInputTextState* state = GetInputTextState(id);
4710
4711
0
    if (g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly)
4712
0
        flags |= ImGuiInputTextFlags_ReadOnly;
4713
0
    const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;
4714
0
    const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;
4715
0
    const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;
4716
0
    const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;
4717
0
    if (is_resizable)
4718
0
        IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!
4719
4720
    // Word-wrapping: enforcing a fixed width not altered by vertical scrollbar makes things easier, notably to track cursor reliably and avoid one-frame glitches.
4721
    // Instead of using ImGuiWindowFlags_AlwaysVerticalScrollbar we account for that space if the scrollbar is not visible.
4722
0
    const bool is_wordwrap = (flags & ImGuiInputTextFlags_WordWrap) != 0;
4723
0
    float wrap_width = 0.0f;
4724
0
    if (is_wordwrap)
4725
0
        wrap_width = ImMax(1.0f, GetContentRegionAvail().x + (draw_window->ScrollbarY ? 0.0f : -g.Style.ScrollbarSize));
4726
4727
0
    const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard)));
4728
4729
0
    const bool user_clicked = hovered && io.MouseClicked[0];
4730
0
    const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
4731
0
    const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);
4732
0
    bool clear_active_id = false;
4733
0
    bool select_all = false;
4734
4735
0
    float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;
4736
4737
0
    const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf);
4738
0
    const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state.
4739
0
    const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav);
4740
0
    const bool init_state = (init_make_active || user_scroll_active);
4741
0
    if (init_reload_from_user_buf)
4742
0
    {
4743
0
        int new_len = (int)ImStrlen(buf);
4744
0
        IM_ASSERT(new_len + 1 <= buf_size && "Is your input buffer properly zero-terminated?");
4745
0
        state->WantReloadUserBuf = false;
4746
0
        InputTextReconcileUndoState(state, state->TextA.Data, state->TextLen, buf, new_len);
4747
0
        state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
4748
0
        state->TextLen = new_len;
4749
0
        memcpy(state->TextA.Data, buf, state->TextLen + 1);
4750
0
        state->Stb->select_start = state->ReloadSelectionStart;
4751
0
        state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd;
4752
0
        state->CursorClamp();
4753
0
    }
4754
0
    else if ((init_state && g.ActiveId != id) || init_changed_specs)
4755
0
    {
4756
        // Access state even if we don't own it yet.
4757
0
        state = &g.InputTextState;
4758
0
        state->CursorAnimReset();
4759
4760
        // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)
4761
0
        InputTextDeactivateHook(state->ID);
4762
4763
        // Take a copy of the initial buffer value.
4764
        // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode)
4765
0
        const int buf_len = (int)ImStrlen(buf);
4766
0
        IM_ASSERT(((buf_len + 1 <= buf_size) || (buf_len == 0 && buf_size == 0)) && "Is your input buffer properly zero-terminated?");
4767
0
        state->TextToRevertTo.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.
4768
0
        memcpy(state->TextToRevertTo.Data, buf, buf_len + 1);
4769
4770
        // Preserve cursor position and undo/redo stack if we come back to same widget
4771
        // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate?
4772
0
        bool recycle_state = (state->ID == id && !init_changed_specs);
4773
0
        if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0)))
4774
0
            recycle_state = false;
4775
4776
        // Start edition
4777
0
        state->ID = id;
4778
0
        state->TextLen = buf_len;
4779
0
        if (!is_readonly)
4780
0
        {
4781
0
            state->TextA.resize(buf_size + 1); // we use +1 to make sure that .Data is always pointing to at least an empty string.
4782
0
            memcpy(state->TextA.Data, buf, state->TextLen + 1);
4783
0
        }
4784
4785
        // Find initial scroll position for right alignment
4786
0
        state->Scroll = ImVec2(0.0f, 0.0f);
4787
0
        if (flags & ImGuiInputTextFlags_ElideLeft)
4788
0
            state->Scroll.x += ImMax(0.0f, CalcTextSize(buf).x - frame_size.x + style.FramePadding.x * 2.0f);
4789
4790
        // Recycle existing cursor/selection/undo stack but clamp position
4791
        // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.
4792
0
        if (recycle_state)
4793
0
            state->CursorClamp();
4794
0
        else
4795
0
            stb_textedit_initialize_state(state->Stb, !is_multiline);
4796
4797
0
        if (!is_multiline)
4798
0
        {
4799
0
            if (flags & ImGuiInputTextFlags_AutoSelectAll)
4800
0
                select_all = true;
4801
0
            if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))
4802
0
                select_all = true;
4803
0
            if (user_clicked && io.KeyCtrl)
4804
0
                select_all = true;
4805
0
        }
4806
4807
0
        if (flags & ImGuiInputTextFlags_AlwaysOverwrite)
4808
0
            state->Stb->insert_mode = 1; // stb field name is indeed incorrect (see #2863)
4809
0
    }
4810
4811
0
    const bool is_osx = io.ConfigMacOSXBehaviors;
4812
0
    if (g.ActiveId != id && init_make_active)
4813
0
    {
4814
0
        IM_ASSERT(state && state->ID == id);
4815
0
        SetActiveID(id, window);
4816
0
        SetFocusID(id, window);
4817
0
        FocusWindow(window);
4818
0
    }
4819
0
    if (g.ActiveId == id)
4820
0
    {
4821
        // Declare some inputs, the other are registered and polled via Shortcut() routing system.
4822
        // FIXME: The reason we don't use Shortcut() is we would need a routing flag to specify multiple mods, or to all mods combination into individual shortcuts.
4823
0
        const ImGuiKey always_owned_keys[] = { ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_Enter, ImGuiKey_KeypadEnter, ImGuiKey_Delete, ImGuiKey_Backspace, ImGuiKey_Home, ImGuiKey_End };
4824
0
        for (ImGuiKey key : always_owned_keys)
4825
0
            SetKeyOwner(key, id);
4826
0
        if (user_clicked)
4827
0
            SetKeyOwner(ImGuiKey_MouseLeft, id);
4828
0
        g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);
4829
0
        if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))
4830
0
        {
4831
0
            g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);
4832
0
            SetKeyOwner(ImGuiKey_UpArrow, id);
4833
0
            SetKeyOwner(ImGuiKey_DownArrow, id);
4834
0
        }
4835
0
        if (is_multiline)
4836
0
        {
4837
0
            SetKeyOwner(ImGuiKey_PageUp, id);
4838
0
            SetKeyOwner(ImGuiKey_PageDown, id);
4839
0
        }
4840
        // FIXME: May be a problem to always steal Alt on OSX, would ideally still allow an uninterrupted Alt down-up to toggle menu
4841
0
        if (is_osx)
4842
0
            SetKeyOwner(ImGuiMod_Alt, id);
4843
4844
        // Expose scroll in a manner that is agnostic to us using a child window
4845
0
        if (is_multiline && state != NULL)
4846
0
            state->Scroll.y = draw_window->Scroll.y;
4847
4848
        // Read-only mode always ever read from source buffer. Refresh TextLen when active.
4849
0
        if (is_readonly && state != NULL)
4850
0
            state->TextLen = (int)ImStrlen(buf);
4851
        //if (is_readonly && state != NULL)
4852
        //    state->TextA.clear(); // Uncomment to facilitate debugging, but we otherwise prefer to keep/amortize th allocation.
4853
0
    }
4854
0
    if (state != NULL)
4855
0
        state->TextSrc = is_readonly ? buf : state->TextA.Data;
4856
4857
    // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)
4858
0
    if (g.ActiveId == id && state == NULL)
4859
0
        ClearActiveID();
4860
4861
    // Release focus when we click outside
4862
0
    if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560
4863
0
        clear_active_id = true;
4864
4865
    // Lock the decision of whether we are going to take the path displaying the cursor or selection
4866
0
    bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active);
4867
0
    bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
4868
0
    bool value_changed = false;
4869
0
    bool validated = false;
4870
4871
    // Select the buffer to render.
4872
0
    const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state;
4873
0
    bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
4874
4875
    // Password pushes a temporary font with only a fallback glyph
4876
0
    if (is_password && !is_displaying_hint)
4877
0
        PushPasswordFont();
4878
4879
    // Word-wrapping: attempt to keep cursor in view while resizing frame/parent
4880
    // FIXME-WORDWRAP: It would be better to preserve same relative offset.
4881
0
    if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width)
4882
0
    {
4883
0
        state->CursorCenterY = true;
4884
0
        state->WrapWidth = wrap_width;
4885
0
        render_cursor = true;
4886
0
    }
4887
4888
    // Process mouse inputs and character inputs
4889
0
    if (g.ActiveId == id)
4890
0
    {
4891
0
        IM_ASSERT(state != NULL);
4892
0
        state->Edited = false;
4893
0
        state->BufCapacity = buf_size;
4894
0
        state->Flags = flags;
4895
0
        state->WrapWidth = wrap_width;
4896
4897
        // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.
4898
        // Down the line we should have a cleaner library-wide concept of Selected vs Active.
4899
0
        g.ActiveIdAllowOverlap = !io.MouseDown[0];
4900
4901
        // Edit in progress
4902
0
        const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->Scroll.x;
4903
0
        const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f));
4904
4905
0
        if (select_all)
4906
0
        {
4907
0
            state->SelectAll();
4908
0
            state->SelectedAllMouseLock = true;
4909
0
        }
4910
0
        else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift)
4911
0
        {
4912
0
            stb_textedit_click(state, state->Stb, mouse_x, mouse_y);
4913
0
            const int multiclick_count = (io.MouseClickedCount[0] - 2);
4914
0
            if ((multiclick_count % 2) == 0)
4915
0
            {
4916
                // Double-click: Select word
4917
                // We always use the "Mac" word advance for double-click select vs CTRL+Right which use the platform dependent variant:
4918
                // FIXME: There are likely many ways to improve this behavior, but there's no "right" behavior (depends on use-case, software, OS)
4919
0
                const bool is_bol = (state->Stb->cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor - 1) == '\n';
4920
0
                if (STB_TEXT_HAS_SELECTION(state->Stb) || !is_bol)
4921
0
                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);
4922
                //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
4923
0
                if (!STB_TEXT_HAS_SELECTION(state->Stb))
4924
0
                    ImStb::stb_textedit_prep_selection_at_cursor(state->Stb);
4925
0
                state->Stb->cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb->cursor);
4926
0
                state->Stb->select_end = state->Stb->cursor;
4927
0
                ImStb::stb_textedit_clamp(state, state->Stb);
4928
0
            }
4929
0
            else
4930
0
            {
4931
                // Triple-click: Select line
4932
0
                const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb->cursor) == '\n';
4933
0
                state->WrapWidth = 0.0f; // Temporarily disable wrapping so we use real line start.
4934
0
                state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART);
4935
0
                state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT);
4936
0
                state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT);
4937
0
                state->WrapWidth = wrap_width;
4938
0
                if (!is_eol && is_multiline)
4939
0
                {
4940
0
                    ImSwap(state->Stb->select_start, state->Stb->select_end);
4941
0
                    state->Stb->cursor = state->Stb->select_end;
4942
0
                }
4943
0
                state->CursorFollow = false;
4944
0
            }
4945
0
            state->CursorAnimReset();
4946
0
        }
4947
0
        else if (io.MouseClicked[0] && !state->SelectedAllMouseLock)
4948
0
        {
4949
0
            if (hovered)
4950
0
            {
4951
0
                if (io.KeyShift)
4952
0
                    stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);
4953
0
                else
4954
0
                    stb_textedit_click(state, state->Stb, mouse_x, mouse_y);
4955
0
                state->CursorAnimReset();
4956
0
            }
4957
0
        }
4958
0
        else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))
4959
0
        {
4960
0
            stb_textedit_drag(state, state->Stb, mouse_x, mouse_y);
4961
0
            state->CursorAnimReset();
4962
0
            state->CursorFollow = true;
4963
0
        }
4964
0
        if (state->SelectedAllMouseLock && !io.MouseDown[0])
4965
0
            state->SelectedAllMouseLock = false;
4966
4967
        // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)
4968
        // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)
4969
0
        if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly)
4970
0
        {
4971
0
            if (Shortcut(ImGuiKey_Tab, ImGuiInputFlags_Repeat, id))
4972
0
            {
4973
0
                unsigned int c = '\t'; // Insert TAB
4974
0
                if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
4975
0
                    state->OnCharPressed(c);
4976
0
            }
4977
            // FIXME: Implement Shift+Tab
4978
            /*
4979
            if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, ImGuiInputFlags_Repeat, id))
4980
            {
4981
            }
4982
            */
4983
0
        }
4984
4985
        // Process regular text input (before we check for Return because using some IME will effectively send a Return?)
4986
        // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.
4987
0
        const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeyCtrl);
4988
0
        if (io.InputQueueCharacters.Size > 0)
4989
0
        {
4990
0
            if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)
4991
0
                for (int n = 0; n < io.InputQueueCharacters.Size; n++)
4992
0
                {
4993
                    // Insert character if they pass filtering
4994
0
                    unsigned int c = (unsigned int)io.InputQueueCharacters[n];
4995
0
                    if (c == '\t') // Skip Tab, see above.
4996
0
                        continue;
4997
0
                    if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
4998
0
                        state->OnCharPressed(c);
4999
0
                }
5000
5001
            // Consume characters
5002
0
            io.InputQueueCharacters.resize(0);
5003
0
        }
5004
0
    }
5005
5006
    // Process other shortcuts/key-presses
5007
0
    bool revert_edit = false;
5008
0
    if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)
5009
0
    {
5010
0
        IM_ASSERT(state != NULL);
5011
5012
0
        const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);
5013
0
        state->Stb->row_count_per_page = row_count_per_page;
5014
5015
0
        const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);
5016
0
        const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl
5017
0
        const bool is_startend_key_down = is_osx && io.KeyCtrl && !io.KeySuper && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End
5018
5019
        // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: former would be handled by InputText)
5020
        // Otherwise we could simply assume that we own the keys as we are active.
5021
0
        const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;
5022
0
        const bool is_cut   = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_X, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, f_repeat, id)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());
5023
0
        const bool is_copy  = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0,        id) || Shortcut(ImGuiMod_Ctrl  | ImGuiKey_Insert, 0,        id)) && !is_password && (!is_multiline || state->HasSelection());
5024
0
        const bool is_paste = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_V, f_repeat, id) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, f_repeat, id)) && !is_readonly;
5025
0
        const bool is_undo  = (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;
5026
0
        const bool is_redo =  (Shortcut(ImGuiMod_Ctrl | ImGuiKey_Y, f_repeat, id) || Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Z, f_repeat, id)) && !is_readonly && is_undoable;
5027
0
        const bool is_select_all = Shortcut(ImGuiMod_Ctrl | ImGuiKey_A, 0, id);
5028
5029
        // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.
5030
0
        const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;
5031
0
        const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true);
5032
0
        const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));
5033
0
        const bool is_cancel = Shortcut(ImGuiKey_Escape, f_repeat, id) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, f_repeat, id));
5034
5035
        // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of.
5036
        // FIXME-OSX: Missing support for Alt(option)+Right/Left = go to end of line, or next line if already in end of line.
5037
0
        if (IsKeyPressed(ImGuiKey_LeftArrow))                        { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
5038
0
        else if (IsKeyPressed(ImGuiKey_RightArrow))                  { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
5039
0
        else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
5040
0
        else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
5041
0
        else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline)      { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }
5042
0
        else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline)    { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }
5043
0
        else if (IsKeyPressed(ImGuiKey_Home))                        { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
5044
0
        else if (IsKeyPressed(ImGuiKey_End))                         { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
5045
0
        else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut)
5046
0
        {
5047
0
            if (!state->HasSelection())
5048
0
            {
5049
                // OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace)
5050
0
                if (is_wordmove_key_down)
5051
0
                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);
5052
0
            }
5053
0
            state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask);
5054
0
        }
5055
0
        else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly)
5056
0
        {
5057
0
            if (!state->HasSelection())
5058
0
            {
5059
0
                if (is_wordmove_key_down)
5060
0
                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT);
5061
0
                else if (is_osx && io.KeyCtrl && !io.KeyAlt && !io.KeySuper)
5062
0
                    state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT);
5063
0
            }
5064
0
            state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);
5065
0
        }
5066
0
        else if (is_enter_pressed || is_gamepad_validate)
5067
0
        {
5068
            // Determine if we turn Enter into a \n character
5069
0
            bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;
5070
0
            if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line != io.KeyCtrl))
5071
0
            {
5072
0
                validated = true;
5073
0
                if (io.ConfigInputTextEnterKeepActive && !is_multiline)
5074
0
                    state->SelectAll(); // No need to scroll
5075
0
                else
5076
0
                    clear_active_id = true;
5077
0
            }
5078
0
            else if (!is_readonly)
5079
0
            {
5080
0
                unsigned int c = '\n'; // Insert new line
5081
0
                if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data))
5082
0
                    state->OnCharPressed(c);
5083
0
            }
5084
0
        }
5085
0
        else if (is_cancel)
5086
0
        {
5087
0
            if (flags & ImGuiInputTextFlags_EscapeClearsAll)
5088
0
            {
5089
0
                if (state->TextA.Data[0] != 0)
5090
0
                {
5091
0
                    revert_edit = true;
5092
0
                }
5093
0
                else
5094
0
                {
5095
0
                    render_cursor = render_selection = false;
5096
0
                    clear_active_id = true;
5097
0
                }
5098
0
            }
5099
0
            else
5100
0
            {
5101
0
                clear_active_id = revert_edit = true;
5102
0
                render_cursor = render_selection = false;
5103
0
            }
5104
0
        }
5105
0
        else if (is_undo || is_redo)
5106
0
        {
5107
0
            state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);
5108
0
            state->ClearSelection();
5109
0
        }
5110
0
        else if (is_select_all)
5111
0
        {
5112
0
            state->SelectAll();
5113
0
            state->CursorFollow = true;
5114
0
        }
5115
0
        else if (is_cut || is_copy)
5116
0
        {
5117
            // Cut, Copy
5118
0
            if (g.PlatformIO.Platform_SetClipboardTextFn != NULL)
5119
0
            {
5120
                // SetClipboardText() only takes null terminated strings + state->TextSrc may point to read-only user buffer, so we need to make a copy.
5121
0
                const int ib = state->HasSelection() ? ImMin(state->Stb->select_start, state->Stb->select_end) : 0;
5122
0
                const int ie = state->HasSelection() ? ImMax(state->Stb->select_start, state->Stb->select_end) : state->TextLen;
5123
0
                g.TempBuffer.reserve(ie - ib + 1);
5124
0
                memcpy(g.TempBuffer.Data, state->TextSrc + ib, ie - ib);
5125
0
                g.TempBuffer.Data[ie - ib] = 0;
5126
0
                SetClipboardText(g.TempBuffer.Data);
5127
0
            }
5128
0
            if (is_cut)
5129
0
            {
5130
0
                if (!state->HasSelection())
5131
0
                    state->SelectAll();
5132
0
                state->CursorFollow = true;
5133
0
                stb_textedit_cut(state, state->Stb);
5134
0
            }
5135
0
        }
5136
0
        else if (is_paste)
5137
0
        {
5138
0
            if (const char* clipboard = GetClipboardText())
5139
0
            {
5140
                // Filter pasted buffer
5141
0
                const int clipboard_len = (int)ImStrlen(clipboard);
5142
0
                ImVector<char> clipboard_filtered;
5143
0
                clipboard_filtered.reserve(clipboard_len + 1);
5144
0
                for (const char* s = clipboard; *s != 0; )
5145
0
                {
5146
0
                    unsigned int c;
5147
0
                    int in_len = ImTextCharFromUtf8(&c, s, NULL);
5148
0
                    s += in_len;
5149
0
                    if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, true))
5150
0
                        continue;
5151
0
                    char c_utf8[5];
5152
0
                    ImTextCharToUtf8(c_utf8, c);
5153
0
                    int out_len = (int)ImStrlen(c_utf8);
5154
0
                    clipboard_filtered.resize(clipboard_filtered.Size + out_len);
5155
0
                    memcpy(clipboard_filtered.Data + clipboard_filtered.Size - out_len, c_utf8, out_len);
5156
0
                }
5157
0
                if (clipboard_filtered.Size > 0) // If everything was filtered, ignore the pasting operation
5158
0
                {
5159
0
                    clipboard_filtered.push_back(0);
5160
0
                    stb_textedit_paste(state, state->Stb, clipboard_filtered.Data, clipboard_filtered.Size - 1);
5161
0
                    state->CursorFollow = true;
5162
0
                }
5163
0
            }
5164
0
        }
5165
5166
        // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame.
5167
0
        render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);
5168
0
    }
5169
5170
    // Process callbacks and apply result back to user's buffer.
5171
0
    const char* apply_new_text = NULL;
5172
0
    int apply_new_text_length = 0;
5173
0
    if (g.ActiveId == id)
5174
0
    {
5175
0
        IM_ASSERT(state != NULL);
5176
0
        if (revert_edit && !is_readonly)
5177
0
        {
5178
0
            if (flags & ImGuiInputTextFlags_EscapeClearsAll)
5179
0
            {
5180
                // Clear input
5181
0
                IM_ASSERT(state->TextA.Data[0] != 0);
5182
0
                apply_new_text = "";
5183
0
                apply_new_text_length = 0;
5184
0
                value_changed = true;
5185
0
                char empty_string = 0;
5186
0
                stb_textedit_replace(state, state->Stb, &empty_string, 0);
5187
0
            }
5188
0
            else if (strcmp(state->TextA.Data, state->TextToRevertTo.Data) != 0)
5189
0
            {
5190
0
                apply_new_text = state->TextToRevertTo.Data;
5191
0
                apply_new_text_length = state->TextToRevertTo.Size - 1;
5192
5193
                // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.
5194
                // Push records into the undo stack so we can CTRL+Z the revert operation itself
5195
0
                value_changed = true;
5196
0
                stb_textedit_replace(state, state->Stb, state->TextToRevertTo.Data, state->TextToRevertTo.Size - 1);
5197
0
            }
5198
0
        }
5199
5200
        // FIXME-OPT: We always reapply the live buffer back to the input buffer before clearing ActiveId,
5201
        // even though strictly speaking it wasn't modified on this frame. Should mark dirty state from the stb_textedit callbacks.
5202
        // If we do that, need to ensure that as special case, 'validated == true' also writes back.
5203
        // This also allows the user to use InputText() without maintaining any user-side storage.
5204
        // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object
5205
        // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize).
5206
0
        const bool apply_edit_back_to_user_buffer = true;// !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);
5207
0
        if (apply_edit_back_to_user_buffer)
5208
0
        {
5209
            // Apply current edited text immediately.
5210
            // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer
5211
5212
            // User callback
5213
0
            if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0)
5214
0
            {
5215
0
                IM_ASSERT(callback != NULL);
5216
5217
                // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.
5218
0
                ImGuiInputTextFlags event_flag = 0;
5219
0
                ImGuiKey event_key = ImGuiKey_None;
5220
0
                if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, 0, id))
5221
0
                {
5222
0
                    event_flag = ImGuiInputTextFlags_CallbackCompletion;
5223
0
                    event_key = ImGuiKey_Tab;
5224
0
                }
5225
0
                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow))
5226
0
                {
5227
0
                    event_flag = ImGuiInputTextFlags_CallbackHistory;
5228
0
                    event_key = ImGuiKey_UpArrow;
5229
0
                }
5230
0
                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow))
5231
0
                {
5232
0
                    event_flag = ImGuiInputTextFlags_CallbackHistory;
5233
0
                    event_key = ImGuiKey_DownArrow;
5234
0
                }
5235
0
                else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited)
5236
0
                {
5237
0
                    event_flag = ImGuiInputTextFlags_CallbackEdit;
5238
0
                }
5239
0
                else if (flags & ImGuiInputTextFlags_CallbackAlways)
5240
0
                {
5241
0
                    event_flag = ImGuiInputTextFlags_CallbackAlways;
5242
0
                }
5243
5244
0
                if (event_flag)
5245
0
                {
5246
0
                    ImGuiInputTextCallbackData callback_data;
5247
0
                    callback_data.Ctx = &g;
5248
0
                    callback_data.EventFlag = event_flag;
5249
0
                    callback_data.Flags = flags;
5250
0
                    callback_data.UserData = callback_user_data;
5251
5252
                    // FIXME-OPT: Undo stack reconcile needs a backup of the data until we rework API, see #7925
5253
0
                    char* callback_buf = is_readonly ? buf : state->TextA.Data;
5254
0
                    IM_ASSERT(callback_buf == state->TextSrc);
5255
0
                    state->CallbackTextBackup.resize(state->TextLen + 1);
5256
0
                    memcpy(state->CallbackTextBackup.Data, callback_buf, state->TextLen + 1);
5257
5258
0
                    callback_data.EventKey = event_key;
5259
0
                    callback_data.Buf = callback_buf;
5260
0
                    callback_data.BufTextLen = state->TextLen;
5261
0
                    callback_data.BufSize = state->BufCapacity;
5262
0
                    callback_data.BufDirty = false;
5263
5264
0
                    const int utf8_cursor_pos = callback_data.CursorPos = state->Stb->cursor;
5265
0
                    const int utf8_selection_start = callback_data.SelectionStart = state->Stb->select_start;
5266
0
                    const int utf8_selection_end = callback_data.SelectionEnd = state->Stb->select_end;
5267
5268
                    // Call user code
5269
0
                    callback(&callback_data);
5270
5271
                    // Read back what user may have modified
5272
0
                    callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback
5273
0
                    IM_ASSERT(callback_data.Buf == callback_buf);         // Invalid to modify those fields
5274
0
                    IM_ASSERT(callback_data.BufSize == state->BufCapacity);
5275
0
                    IM_ASSERT(callback_data.Flags == flags);
5276
0
                    const bool buf_dirty = callback_data.BufDirty;
5277
0
                    if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty)            { state->Stb->cursor = callback_data.CursorPos; state->CursorFollow = true; }
5278
0
                    if (callback_data.SelectionStart != utf8_selection_start || buf_dirty)  { state->Stb->select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb->cursor : callback_data.SelectionStart; }
5279
0
                    if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty)      { state->Stb->select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb->select_start : callback_data.SelectionEnd; }
5280
0
                    if (buf_dirty)
5281
0
                    {
5282
                        // Callback may update buffer and thus set buf_dirty even in read-only mode.
5283
0
                        IM_ASSERT(callback_data.BufTextLen == (int)ImStrlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!
5284
0
                        InputTextReconcileUndoState(state, state->CallbackTextBackup.Data, state->CallbackTextBackup.Size - 1, callback_data.Buf, callback_data.BufTextLen);
5285
0
                        state->TextLen = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()
5286
0
                        state->CursorAnimReset();
5287
0
                    }
5288
0
                }
5289
0
            }
5290
5291
            // Will copy result string if modified
5292
0
            if (!is_readonly && strcmp(state->TextSrc, buf) != 0)
5293
0
            {
5294
0
                apply_new_text = state->TextSrc;
5295
0
                apply_new_text_length = state->TextLen;
5296
0
                value_changed = true;
5297
0
            }
5298
0
        }
5299
0
    }
5300
5301
    // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details)
5302
0
    if (g.InputTextDeactivatedState.ID == id)
5303
0
    {
5304
0
        if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0)
5305
0
        {
5306
0
            apply_new_text = g.InputTextDeactivatedState.TextA.Data;
5307
0
            apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1;
5308
0
            value_changed = true;
5309
            //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text);
5310
0
        }
5311
0
        g.InputTextDeactivatedState.ID = 0;
5312
0
    }
5313
5314
    // Copy result to user buffer. This can currently only happen when (g.ActiveId == id)
5315
0
    if (apply_new_text != NULL)
5316
0
    {
5317
        //// We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size
5318
        //// of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used
5319
        //// without any storage on user's side.
5320
0
        IM_ASSERT(apply_new_text_length >= 0);
5321
0
        if (is_resizable)
5322
0
        {
5323
0
            ImGuiInputTextCallbackData callback_data;
5324
0
            callback_data.Ctx = &g;
5325
0
            callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;
5326
0
            callback_data.Flags = flags;
5327
0
            callback_data.Buf = buf;
5328
0
            callback_data.BufTextLen = apply_new_text_length;
5329
0
            callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);
5330
0
            callback_data.UserData = callback_user_data;
5331
0
            callback(&callback_data);
5332
0
            buf = callback_data.Buf;
5333
0
            buf_size = callback_data.BufSize;
5334
0
            apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);
5335
0
            IM_ASSERT(apply_new_text_length <= buf_size);
5336
0
        }
5337
        //IMGUI_DEBUG_PRINT("InputText(\"%s\"): apply_new_text length %d\n", label, apply_new_text_length);
5338
5339
        // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.
5340
0
        ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));
5341
0
    }
5342
5343
    // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)
5344
    // Otherwise request text input ahead for next frame.
5345
0
    if (g.ActiveId == id && clear_active_id)
5346
0
        ClearActiveID();
5347
5348
    // Render frame
5349
0
    if (!is_multiline)
5350
0
    {
5351
0
        RenderNavCursor(frame_bb, id);
5352
0
        RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
5353
0
    }
5354
5355
0
    ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;
5356
0
    ImVec2 text_size(0.0f, 0.0f);
5357
0
    ImRect clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size
5358
0
    if (is_multiline)
5359
0
        clip_rect.ClipWith(draw_window->ClipRect);
5360
5361
    // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line
5362
    // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.
5363
    // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.
5364
0
    const int buf_display_max_length = 2 * 1024 * 1024;
5365
0
    const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595
5366
0
    const char* buf_display_end = NULL; // We have specialized paths below for setting the length
5367
5368
    // Display hint when contents is empty
5369
    // At this point we need to handle the possibility that a callback could have modified the underlying buffer (#8368)
5370
0
    const bool new_is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);
5371
0
    if (new_is_displaying_hint != is_displaying_hint)
5372
0
    {
5373
0
        if (is_password && !is_displaying_hint)
5374
0
            PopPasswordFont();
5375
0
        is_displaying_hint = new_is_displaying_hint;
5376
0
        if (is_password && !is_displaying_hint)
5377
0
            PushPasswordFont();
5378
0
    }
5379
0
    if (is_displaying_hint)
5380
0
    {
5381
0
        buf_display = hint;
5382
0
        buf_display_end = hint + ImStrlen(hint);
5383
0
    }
5384
0
    else
5385
0
    {
5386
0
        if (render_cursor || render_selection || g.ActiveId == id)
5387
0
            buf_display_end = buf_display + state->TextLen; //-V595
5388
0
        else if (is_multiline && !is_wordwrap)
5389
0
            buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path.
5390
0
        else
5391
0
            buf_display_end = buf_display + ImStrlen(buf_display);
5392
0
    }
5393
5394
    // Calculate visibility
5395
0
    int line_visible_n0 = 0, line_visible_n1 = 1;
5396
0
    if (is_multiline)
5397
0
        CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);
5398
5399
    // Build line index for easy data access (makes code below simpler and faster)
5400
0
    ImGuiTextIndex* line_index = &g.InputTextLineIndex;
5401
0
    line_index->Offsets.resize(0);
5402
0
    int line_count = 1;
5403
0
    if (is_multiline)
5404
0
        line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, (render_cursor && state && state->CursorFollow) ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end);
5405
0
    line_index->EndOffset = (int)(buf_display_end - buf_display);
5406
0
    line_visible_n1 = ImMin(line_visible_n1, line_count);
5407
5408
    // Store text height (we don't need width)
5409
0
    text_size = ImVec2(inner_size.x, line_count * g.FontSize);
5410
    //GetForegroundDrawList()->AddRect(draw_pos + ImVec2(0, line_visible_n0 * g.FontSize), draw_pos + ImVec2(frame_size.x, line_visible_n1 * g.FontSize), IM_COL32(255, 0, 0, 255));
5411
5412
    // Calculate blinking cursor position
5413
0
    const ImVec2 cursor_offset = render_cursor && state ? InputTextLineIndexGetPosOffset(g, state, line_index, buf_display, buf_display_end, state->Stb->cursor) : ImVec2(0.0f, 0.0f);
5414
0
    ImVec2 draw_scroll;
5415
5416
    // Render text. We currently only render selection when the widget is active or while scrolling.
5417
0
    const ImU32 text_col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);
5418
0
    if (render_cursor || render_selection)
5419
0
    {
5420
        // Render text (with cursor and selection)
5421
        // This is going to be messy. We need to:
5422
        // - Display the text (this alone can be more easily clipped)
5423
        // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)
5424
        // - Measure text height (for scrollbar)
5425
        // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)
5426
        // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.
5427
0
        IM_ASSERT(state != NULL);
5428
0
        state->LineCount = line_count;
5429
5430
        // Scroll
5431
0
        float new_scroll_y = scroll_y;
5432
0
        if (render_cursor && state->CursorFollow)
5433
0
        {
5434
            // Horizontal scroll in chunks of quarter width
5435
0
            if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))
5436
0
            {
5437
0
                const float scroll_increment_x = inner_size.x * 0.25f;
5438
0
                const float visible_width = inner_size.x - style.FramePadding.x;
5439
0
                if (cursor_offset.x < state->Scroll.x)
5440
0
                    state->Scroll.x = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x));
5441
0
                else if (cursor_offset.x - visible_width >= state->Scroll.x)
5442
0
                    state->Scroll.x = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x);
5443
0
            }
5444
0
            else
5445
0
            {
5446
0
                state->Scroll.x = 0.0f;
5447
0
            }
5448
5449
            // Vertical scroll
5450
0
            if (is_multiline)
5451
0
            {
5452
                // Test if cursor is vertically visible
5453
0
                if (cursor_offset.y - g.FontSize < scroll_y)
5454
0
                    new_scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);
5455
0
                else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)
5456
0
                    new_scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;
5457
0
            }
5458
0
            state->CursorFollow = false;
5459
0
        }
5460
0
        if (state->CursorCenterY)
5461
0
        {
5462
0
            if (is_multiline)
5463
0
                new_scroll_y = cursor_offset.y - g.FontSize - (inner_size.y * 0.5f - style.FramePadding.y);
5464
0
            state->CursorCenterY = false;
5465
0
            render_cursor = false;
5466
0
        }
5467
0
        if (new_scroll_y != scroll_y)
5468
0
        {
5469
0
            const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);
5470
0
            scroll_y = ImClamp(new_scroll_y, 0.0f, scroll_max_y);
5471
0
            draw_pos.y += (draw_window->Scroll.y - scroll_y);   // Manipulate cursor pos immediately avoid a frame of lag
5472
0
            draw_window->Scroll.y = scroll_y;
5473
0
            CalcClipRectVisibleItemsY(clip_rect, draw_pos, g.FontSize, &line_visible_n0, &line_visible_n1);
5474
0
            line_visible_n1 = ImMin(line_visible_n1, line_count);
5475
0
        }
5476
5477
        // Draw selection
5478
0
        draw_scroll.x = state->Scroll.x;
5479
0
        if (render_selection)
5480
0
        {
5481
0
            const ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.
5482
0
            const float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.
5483
0
            const float bg_offy_dn = is_multiline ? 0.0f : 2.0f;
5484
0
            const float bg_eol_width = IM_TRUNC(g.FontBaked->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
5485
5486
0
            const char* text_selected_begin = buf_display + ImMin(state->Stb->select_start, state->Stb->select_end);
5487
0
            const char* text_selected_end = buf_display + ImMax(state->Stb->select_start, state->Stb->select_end);
5488
0
            for (int line_n = line_visible_n0; line_n < line_visible_n1; line_n++)
5489
0
            {
5490
0
                const char* p = line_index->get_line_begin(buf_display, line_n);
5491
0
                const char* p_eol = line_index->get_line_end(buf_display, line_n);
5492
0
                const bool p_eol_is_wrap = (p_eol < buf_display_end && *p_eol != '\n');
5493
0
                if (p_eol_is_wrap)
5494
0
                    p_eol++;
5495
0
                const char* line_selected_begin = (text_selected_begin > p) ? text_selected_begin : p;
5496
0
                const char* line_selected_end = (text_selected_end < p_eol) ? text_selected_end : p_eol;
5497
5498
0
                float rect_width = 0.0f;
5499
0
                if (line_selected_begin < line_selected_end)
5500
0
                    rect_width += CalcTextSize(line_selected_begin, line_selected_end).x;
5501
0
                if (text_selected_begin <= p_eol && text_selected_end > p_eol && !p_eol_is_wrap)
5502
0
                    rect_width += bg_eol_width; // So we can see selected empty lines
5503
0
                if (rect_width == 0.0f)
5504
0
                    continue;
5505
5506
0
                ImRect rect;
5507
0
                rect.Min.x = draw_pos.x - draw_scroll.x + CalcTextSize(p, line_selected_begin).x;
5508
0
                rect.Min.y = draw_pos.y - draw_scroll.y + line_n * g.FontSize;
5509
0
                rect.Max.x = rect.Min.x + rect_width;
5510
0
                rect.Max.y = rect.Min.y + bg_offy_dn + g.FontSize;
5511
0
                rect.Min.y -= bg_offy_up;
5512
0
                rect.ClipWith(clip_rect);
5513
0
                draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);
5514
0
            }
5515
0
        }
5516
0
    }
5517
5518
    // Find render position for right alignment (single-line only)
5519
0
    if (g.ActiveId != id && flags & ImGuiInputTextFlags_ElideLeft)
5520
0
        draw_pos.x = ImMin(draw_pos.x, frame_bb.Max.x - CalcTextSize(buf_display, NULL).x - style.FramePadding.x);
5521
    //draw_scroll.x = state->Scroll.x; // Preserve scroll when inactive?
5522
5523
    // Render text
5524
0
    if ((is_multiline || (buf_display_end - buf_display) < buf_display_max_length) && (text_col & IM_COL32_A_MASK) && (line_visible_n0 < line_visible_n1))
5525
0
        g.Font->RenderText(draw_window->DrawList, g.FontSize,
5526
0
            draw_pos - draw_scroll + ImVec2(0.0f, line_visible_n0 * g.FontSize),
5527
0
            text_col, clip_rect.AsVec4(),
5528
0
            line_index->get_line_begin(buf_display, line_visible_n0),
5529
0
            line_index->get_line_end(buf_display, line_visible_n1 - 1),
5530
0
            wrap_width, ImDrawTextFlags_WrapKeepBlanks | ImDrawTextFlags_CpuFineClip);
5531
5532
    // Render blinking cursor
5533
0
    if (render_cursor)
5534
0
    {
5535
0
        state->CursorAnim += io.DeltaTime;
5536
0
        bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
5537
0
        ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
5538
0
        ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
5539
0
        if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
5540
0
            draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_InputTextCursor), 1.0f); // FIXME-DPI: Cursor thickness (#7031)
5541
5542
        // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)
5543
        // This is required for some backends (SDL3) to start emitting character/text inputs.
5544
        // As per #6341, make sure we don't set that on the deactivating frame.
5545
0
        if (!is_readonly && g.ActiveId == id)
5546
0
        {
5547
0
            ImGuiPlatformImeData* ime_data = &g.PlatformImeData; // (this is a public struct, passed to io.Platform_SetImeDataFn() handler)
5548
0
            ime_data->WantVisible = true;
5549
0
            ime_data->WantTextInput = true;
5550
0
            ime_data->InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);
5551
0
            ime_data->InputLineHeight = g.FontSize;
5552
0
            ime_data->ViewportId = window->Viewport->ID;
5553
0
        }
5554
0
    }
5555
5556
0
    if (is_password && !is_displaying_hint)
5557
0
        PopPasswordFont();
5558
5559
0
    if (is_multiline)
5560
0
    {
5561
        // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (see #4761, #7870)...
5562
0
        Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y));
5563
0
        g.NextItemData.ItemFlags |= (ImGuiItemFlags)ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop;
5564
0
        EndChild();
5565
0
        item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow);
5566
5567
        // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active...
5568
        // FIXME: This quite messy/tricky, should attempt to get rid of the child window.
5569
0
        EndGroup();
5570
0
        if (g.LastItemData.ID == 0 || g.LastItemData.ID != GetWindowScrollbarID(draw_window, ImGuiAxis_Y))
5571
0
        {
5572
0
            g.LastItemData.ID = id;
5573
0
            g.LastItemData.ItemFlags = item_data_backup.ItemFlags;
5574
0
            g.LastItemData.StatusFlags = item_data_backup.StatusFlags;
5575
0
        }
5576
0
    }
5577
0
    if (state)
5578
0
        state->TextSrc = NULL;
5579
5580
    // Log as text
5581
0
    if (g.LogEnabled && (!is_password || is_displaying_hint))
5582
0
    {
5583
0
        LogSetNextTextDecoration("{", "}");
5584
0
        LogRenderedText(&draw_pos, buf_display, buf_display_end);
5585
0
    }
5586
5587
0
    if (label_size.x > 0)
5588
0
        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
5589
5590
0
    if (value_changed)
5591
0
        MarkItemEdited(id);
5592
5593
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);
5594
0
    if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)
5595
0
        return validated;
5596
0
    else
5597
0
        return value_changed;
5598
0
}
5599
5600
void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
5601
0
{
5602
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
5603
0
    ImGuiContext& g = *GImGui;
5604
0
    ImStb::STB_TexteditState* stb_state = state->Stb;
5605
0
    ImStb::StbUndoState* undo_state = &stb_state->undostate;
5606
0
    Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId);
5607
0
    DebugLocateItemOnHover(state->ID);
5608
0
    Text("TextLen: %d, Cursor: %d%s, Selection: %d..%d", state->TextLen, stb_state->cursor,
5609
0
        (state->Flags & ImGuiInputTextFlags_WordWrap) ? (state->LastMoveDirectionLR == ImGuiDir_Left ? " (L)" : " (R)") : "",
5610
0
        stb_state->select_start, stb_state->select_end);
5611
0
    Text("BufCapacity: %d, LineCount: %d", state->BufCapacity, state->LineCount);
5612
0
    Text("(Internal Buffer: TextA Size: %d, Capacity: %d)", state->TextA.Size, state->TextA.Capacity);
5613
0
    Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x);
5614
0
    Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);
5615
0
    if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) // Visualize undo state
5616
0
    {
5617
0
        PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
5618
0
        for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++)
5619
0
        {
5620
0
            ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n];
5621
0
            const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';
5622
0
            if (undo_rec_type == ' ')
5623
0
                BeginDisabled();
5624
0
            const int buf_preview_len = (undo_rec_type != ' ' && undo_rec->char_storage != -1) ? undo_rec->insert_length : 0;
5625
0
            const char* buf_preview_str = undo_state->undo_char + undo_rec->char_storage;
5626
0
            Text("%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \"%.*s\"",
5627
0
                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf_preview_len, buf_preview_str);
5628
0
            if (undo_rec_type == ' ')
5629
0
                EndDisabled();
5630
0
        }
5631
0
        PopStyleVar();
5632
0
    }
5633
0
    EndChild();
5634
#else
5635
    IM_UNUSED(state);
5636
#endif
5637
0
}
5638
5639
//-------------------------------------------------------------------------
5640
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
5641
//-------------------------------------------------------------------------
5642
// - ColorEdit3()
5643
// - ColorEdit4()
5644
// - ColorPicker3()
5645
// - RenderColorRectWithAlphaCheckerboard() [Internal]
5646
// - ColorPicker4()
5647
// - ColorButton()
5648
// - SetColorEditOptions()
5649
// - ColorTooltip() [Internal]
5650
// - ColorEditOptionsPopup() [Internal]
5651
// - ColorPickerOptionsPopup() [Internal]
5652
//-------------------------------------------------------------------------
5653
5654
bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)
5655
0
{
5656
0
    return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);
5657
0
}
5658
5659
static void ColorEditRestoreH(const float* col, float* H)
5660
0
{
5661
0
    ImGuiContext& g = *GImGui;
5662
0
    IM_ASSERT(g.ColorEditCurrentID != 0);
5663
0
    if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
5664
0
        return;
5665
0
    *H = g.ColorEditSavedHue;
5666
0
}
5667
5668
// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation.
5669
// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting.
5670
static void ColorEditRestoreHS(const float* col, float* H, float* S, float* V)
5671
0
{
5672
0
    ImGuiContext& g = *GImGui;
5673
0
    IM_ASSERT(g.ColorEditCurrentID != 0);
5674
0
    if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))
5675
0
        return;
5676
5677
    // When S == 0, H is undefined.
5678
    // When H == 1 it wraps around to 0.
5679
0
    if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1))
5680
0
        *H = g.ColorEditSavedHue;
5681
5682
    // When V == 0, S is undefined.
5683
0
    if (*V == 0.0f)
5684
0
        *S = g.ColorEditSavedSat;
5685
0
}
5686
5687
// Edit colors components (each component in 0.0f..1.0f range).
5688
// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
5689
// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL+Click over input fields to edit them and TAB to go to next item.
5690
bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)
5691
0
{
5692
0
    ImGuiWindow* window = GetCurrentWindow();
5693
0
    if (window->SkipItems)
5694
0
        return false;
5695
5696
0
    ImGuiContext& g = *GImGui;
5697
0
    const ImGuiStyle& style = g.Style;
5698
0
    const float square_sz = GetFrameHeight();
5699
0
    const char* label_display_end = FindRenderedTextEnd(label);
5700
0
    float w_full = CalcItemWidth();
5701
0
    g.NextItemData.ClearFlags();
5702
5703
0
    BeginGroup();
5704
0
    PushID(label);
5705
0
    const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);
5706
0
    if (set_current_color_edit_id)
5707
0
        g.ColorEditCurrentID = window->IDStack.back();
5708
5709
    // If we're not showing any slider there's no point in doing any HSV conversions
5710
0
    const ImGuiColorEditFlags flags_untouched = flags;
5711
0
    if (flags & ImGuiColorEditFlags_NoInputs)
5712
0
        flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions;
5713
5714
    // Context menu: display and modify options (before defaults are applied)
5715
0
    if (!(flags & ImGuiColorEditFlags_NoOptions))
5716
0
        ColorEditOptionsPopup(col, flags);
5717
5718
    // Read stored options
5719
0
    if (!(flags & ImGuiColorEditFlags_DisplayMask_))
5720
0
        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_);
5721
0
    if (!(flags & ImGuiColorEditFlags_DataTypeMask_))
5722
0
        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_);
5723
0
    if (!(flags & ImGuiColorEditFlags_PickerMask_))
5724
0
        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_);
5725
0
    if (!(flags & ImGuiColorEditFlags_InputMask_))
5726
0
        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_);
5727
0
    flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));
5728
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected
5729
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));   // Check that only 1 is selected
5730
5731
0
    const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;
5732
0
    const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;
5733
0
    const int components = alpha ? 4 : 3;
5734
0
    const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);
5735
0
    const float w_inputs = ImMax(w_full - w_button, 1.0f);
5736
0
    w_full = w_inputs + w_button;
5737
5738
    // Convert to the formats we need
5739
0
    float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };
5740
0
    if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB))
5741
0
        ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
5742
0
    else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV))
5743
0
    {
5744
        // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.
5745
0
        ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
5746
0
        ColorEditRestoreHS(col, &f[0], &f[1], &f[2]);
5747
0
    }
5748
0
    int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };
5749
5750
0
    bool value_changed = false;
5751
0
    bool value_changed_as_float = false;
5752
5753
0
    const ImVec2 pos = window->DC.CursorPos;
5754
0
    const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f;
5755
0
    window->DC.CursorPos.x = pos.x + inputs_offset_x;
5756
5757
0
    if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
5758
0
    {
5759
        // RGB/HSV 0..255 Sliders
5760
0
        const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1);
5761
5762
0
        const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
5763
0
        static const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
5764
0
        static const char* fmt_table_int[3][4] =
5765
0
        {
5766
0
            {   "%3d",   "%3d",   "%3d",   "%3d" }, // Short display
5767
0
            { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA
5768
0
            { "H:%3d", "S:%3d", "V:%3d", "A:%3d" }  // Long display for HSVA
5769
0
        };
5770
0
        static const char* fmt_table_float[3][4] =
5771
0
        {
5772
0
            {   "%0.3f",   "%0.3f",   "%0.3f",   "%0.3f" }, // Short display
5773
0
            { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA
5774
0
            { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" }  // Long display for HSVA
5775
0
        };
5776
0
        const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1;
5777
5778
0
        float prev_split = 0.0f;
5779
0
        for (int n = 0; n < components; n++)
5780
0
        {
5781
0
            if (n > 0)
5782
0
                SameLine(0, style.ItemInnerSpacing.x);
5783
0
            float next_split = IM_TRUNC(w_items * (n + 1) / components);
5784
0
            SetNextItemWidth(ImMax(next_split - prev_split, 1.0f));
5785
0
            prev_split = next_split;
5786
5787
            // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.
5788
0
            if (flags & ImGuiColorEditFlags_Float)
5789
0
            {
5790
0
                value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);
5791
0
                value_changed_as_float |= value_changed;
5792
0
            }
5793
0
            else
5794
0
            {
5795
0
                value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);
5796
0
            }
5797
0
            if (!(flags & ImGuiColorEditFlags_NoOptions))
5798
0
                OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
5799
0
        }
5800
0
    }
5801
0
    else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
5802
0
    {
5803
        // RGB Hexadecimal Input
5804
0
        char buf[64];
5805
0
        if (alpha)
5806
0
            ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255));
5807
0
        else
5808
0
            ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));
5809
0
        SetNextItemWidth(w_inputs);
5810
0
        if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase))
5811
0
        {
5812
0
            value_changed = true;
5813
0
            char* p = buf;
5814
0
            while (*p == '#' || ImCharIsBlankA(*p))
5815
0
                p++;
5816
0
            i[0] = i[1] = i[2] = 0;
5817
0
            i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha)
5818
0
            int r;
5819
0
            if (alpha)
5820
0
                r = sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)
5821
0
            else
5822
0
                r = sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);
5823
0
            IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'.
5824
0
        }
5825
0
        if (!(flags & ImGuiColorEditFlags_NoOptions))
5826
0
            OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
5827
0
    }
5828
5829
0
    ImGuiWindow* picker_active_window = NULL;
5830
0
    if (!(flags & ImGuiColorEditFlags_NoSmallPreview))
5831
0
    {
5832
0
        const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x;
5833
0
        window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y);
5834
5835
0
        const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);
5836
0
        if (ColorButton("##ColorButton", col_v4, flags))
5837
0
        {
5838
0
            if (!(flags & ImGuiColorEditFlags_NoPicker))
5839
0
            {
5840
                // Store current color and open a picker
5841
0
                g.ColorPickerRef = col_v4;
5842
0
                OpenPopup("picker");
5843
0
                SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y));
5844
0
            }
5845
0
        }
5846
0
        if (!(flags & ImGuiColorEditFlags_NoOptions))
5847
0
            OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
5848
5849
0
        if (BeginPopup("picker"))
5850
0
        {
5851
0
            if (g.CurrentWindow->BeginCount == 1)
5852
0
            {
5853
0
                picker_active_window = g.CurrentWindow;
5854
0
                if (label != label_display_end)
5855
0
                {
5856
0
                    TextEx(label, label_display_end);
5857
0
                    Spacing();
5858
0
                }
5859
0
                ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;
5860
0
                ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;
5861
0
                SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?
5862
0
                value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x);
5863
0
            }
5864
0
            EndPopup();
5865
0
        }
5866
0
    }
5867
5868
0
    if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))
5869
0
    {
5870
        // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left),
5871
        // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this.
5872
0
        SameLine(0.0f, style.ItemInnerSpacing.x);
5873
0
        window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x);
5874
0
        TextEx(label, label_display_end);
5875
0
    }
5876
5877
    // Convert back
5878
0
    if (value_changed && picker_active_window == NULL)
5879
0
    {
5880
0
        if (!value_changed_as_float)
5881
0
            for (int n = 0; n < 4; n++)
5882
0
                f[n] = i[n] / 255.0f;
5883
0
        if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB))
5884
0
        {
5885
0
            g.ColorEditSavedHue = f[0];
5886
0
            g.ColorEditSavedSat = f[1];
5887
0
            ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);
5888
0
            g.ColorEditSavedID = g.ColorEditCurrentID;
5889
0
            g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));
5890
0
        }
5891
0
        if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV))
5892
0
            ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);
5893
5894
0
        col[0] = f[0];
5895
0
        col[1] = f[1];
5896
0
        col[2] = f[2];
5897
0
        if (alpha)
5898
0
            col[3] = f[3];
5899
0
    }
5900
5901
0
    if (set_current_color_edit_id)
5902
0
        g.ColorEditCurrentID = 0;
5903
0
    PopID();
5904
0
    EndGroup();
5905
5906
    // Drag and Drop Target
5907
    // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.
5908
0
    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.ItemFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())
5909
0
    {
5910
0
        bool accepted_drag_drop = false;
5911
0
        if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
5912
0
        {
5913
0
            memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 //-V1086
5914
0
            value_changed = accepted_drag_drop = true;
5915
0
        }
5916
0
        if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
5917
0
        {
5918
0
            memcpy((float*)col, payload->Data, sizeof(float) * components);
5919
0
            value_changed = accepted_drag_drop = true;
5920
0
        }
5921
5922
        // Drag-drop payloads are always RGB
5923
0
        if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV))
5924
0
            ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]);
5925
0
        EndDragDropTarget();
5926
0
    }
5927
5928
    // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().
5929
0
    if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)
5930
0
        g.LastItemData.ID = g.ActiveId;
5931
5932
0
    if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
5933
0
        MarkItemEdited(g.LastItemData.ID);
5934
5935
0
    return value_changed;
5936
0
}
5937
5938
bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)
5939
0
{
5940
0
    float col4[4] = { col[0], col[1], col[2], 1.0f };
5941
0
    if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))
5942
0
        return false;
5943
0
    col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];
5944
0
    return true;
5945
0
}
5946
5947
// Helper for ColorPicker4()
5948
static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha)
5949
0
{
5950
0
    ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha);
5951
0
    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1,         pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8));
5952
0
    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x,             pos.y), half_sz,                              ImGuiDir_Right, IM_COL32(255,255,255,alpha8));
5953
0
    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left,  IM_COL32(0,0,0,alpha8));
5954
0
    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x,     pos.y), half_sz,                              ImGuiDir_Left,  IM_COL32(255,255,255,alpha8));
5955
0
}
5956
5957
// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
5958
// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.)
5959
// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)
5960
// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0)
5961
bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)
5962
0
{
5963
0
    ImGuiContext& g = *GImGui;
5964
0
    ImGuiWindow* window = GetCurrentWindow();
5965
0
    if (window->SkipItems)
5966
0
        return false;
5967
5968
0
    ImDrawList* draw_list = window->DrawList;
5969
0
    ImGuiStyle& style = g.Style;
5970
0
    ImGuiIO& io = g.IO;
5971
5972
0
    const float width = CalcItemWidth();
5973
0
    const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0;
5974
0
    g.NextItemData.ClearFlags();
5975
5976
0
    PushID(label);
5977
0
    const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);
5978
0
    if (set_current_color_edit_id)
5979
0
        g.ColorEditCurrentID = window->IDStack.back();
5980
0
    BeginGroup();
5981
5982
0
    if (!(flags & ImGuiColorEditFlags_NoSidePreview))
5983
0
        flags |= ImGuiColorEditFlags_NoSmallPreview;
5984
5985
    // Context menu: display and store options.
5986
0
    if (!(flags & ImGuiColorEditFlags_NoOptions))
5987
0
        ColorPickerOptionsPopup(col, flags);
5988
5989
    // Read stored options
5990
0
    if (!(flags & ImGuiColorEditFlags_PickerMask_))
5991
0
        flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;
5992
0
    if (!(flags & ImGuiColorEditFlags_InputMask_))
5993
0
        flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;
5994
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected
5995
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));  // Check that only 1 is selected
5996
0
    if (!(flags & ImGuiColorEditFlags_NoOptions))
5997
0
        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);
5998
5999
    // Setup
6000
0
    int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;
6001
0
    bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);
6002
0
    ImVec2 picker_pos = window->DC.CursorPos;
6003
0
    float square_sz = GetFrameHeight();
6004
0
    float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars
6005
0
    float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
6006
0
    float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
6007
0
    float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
6008
0
    float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f);
6009
6010
0
    float backup_initial_col[4];
6011
0
    memcpy(backup_initial_col, col, components * sizeof(float));
6012
6013
0
    float wheel_thickness = sv_picker_size * 0.08f;
6014
0
    float wheel_r_outer = sv_picker_size * 0.50f;
6015
0
    float wheel_r_inner = wheel_r_outer - wheel_thickness;
6016
0
    ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f);
6017
6018
    // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.
6019
0
    float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);
6020
0
    ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.
6021
0
    ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.
6022
0
    ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.
6023
6024
0
    float H = col[0], S = col[1], V = col[2];
6025
0
    float R = col[0], G = col[1], B = col[2];
6026
0
    if (flags & ImGuiColorEditFlags_InputRGB)
6027
0
    {
6028
        // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.
6029
0
        ColorConvertRGBtoHSV(R, G, B, H, S, V);
6030
0
        ColorEditRestoreHS(col, &H, &S, &V);
6031
0
    }
6032
0
    else if (flags & ImGuiColorEditFlags_InputHSV)
6033
0
    {
6034
0
        ColorConvertHSVtoRGB(H, S, V, R, G, B);
6035
0
    }
6036
6037
0
    bool value_changed = false, value_changed_h = false, value_changed_sv = false;
6038
6039
0
    PushItemFlag(ImGuiItemFlags_NoNav, true);
6040
0
    if (flags & ImGuiColorEditFlags_PickerHueWheel)
6041
0
    {
6042
        // Hue wheel + SV triangle logic
6043
0
        InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));
6044
0
        if (IsItemActive() && !is_readonly)
6045
0
        {
6046
0
            ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;
6047
0
            ImVec2 current_off = g.IO.MousePos - wheel_center;
6048
0
            float initial_dist2 = ImLengthSqr(initial_off);
6049
0
            if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1))
6050
0
            {
6051
                // Interactive with Hue wheel
6052
0
                H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f;
6053
0
                if (H < 0.0f)
6054
0
                    H += 1.0f;
6055
0
                value_changed = value_changed_h = true;
6056
0
            }
6057
0
            float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);
6058
0
            float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);
6059
0
            if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))
6060
0
            {
6061
                // Interacting with SV triangle
6062
0
                ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);
6063
0
                if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))
6064
0
                    current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);
6065
0
                float uu, vv, ww;
6066
0
                ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);
6067
0
                V = ImClamp(1.0f - vv, 0.0001f, 1.0f);
6068
0
                S = ImClamp(uu / V, 0.0001f, 1.0f);
6069
0
                value_changed = value_changed_sv = true;
6070
0
            }
6071
0
        }
6072
0
        if (!(flags & ImGuiColorEditFlags_NoOptions))
6073
0
            OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
6074
0
    }
6075
0
    else if (flags & ImGuiColorEditFlags_PickerHueBar)
6076
0
    {
6077
        // SV rectangle logic
6078
0
        InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size));
6079
0
        if (IsItemActive() && !is_readonly)
6080
0
        {
6081
0
            S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));
6082
0
            V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
6083
0
            ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.
6084
0
            value_changed = value_changed_sv = true;
6085
0
        }
6086
0
        if (!(flags & ImGuiColorEditFlags_NoOptions))
6087
0
            OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight);
6088
6089
        // Hue bar logic
6090
0
        SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));
6091
0
        InvisibleButton("hue", ImVec2(bars_width, sv_picker_size));
6092
0
        if (IsItemActive() && !is_readonly)
6093
0
        {
6094
0
            H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
6095
0
            value_changed = value_changed_h = true;
6096
0
        }
6097
0
    }
6098
6099
    // Alpha bar logic
6100
0
    if (alpha_bar)
6101
0
    {
6102
0
        SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));
6103
0
        InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size));
6104
0
        if (IsItemActive())
6105
0
        {
6106
0
            col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));
6107
0
            value_changed = true;
6108
0
        }
6109
0
    }
6110
0
    PopItemFlag(); // ImGuiItemFlags_NoNav
6111
6112
0
    if (!(flags & ImGuiColorEditFlags_NoSidePreview))
6113
0
    {
6114
0
        SameLine(0, style.ItemInnerSpacing.x);
6115
0
        BeginGroup();
6116
0
    }
6117
6118
0
    if (!(flags & ImGuiColorEditFlags_NoLabel))
6119
0
    {
6120
0
        const char* label_display_end = FindRenderedTextEnd(label);
6121
0
        if (label != label_display_end)
6122
0
        {
6123
0
            if ((flags & ImGuiColorEditFlags_NoSidePreview))
6124
0
                SameLine(0, style.ItemInnerSpacing.x);
6125
0
            TextEx(label, label_display_end);
6126
0
        }
6127
0
    }
6128
6129
0
    if (!(flags & ImGuiColorEditFlags_NoSidePreview))
6130
0
    {
6131
0
        PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);
6132
0
        ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
6133
0
        if ((flags & ImGuiColorEditFlags_NoLabel))
6134
0
            Text("Current");
6135
6136
0
        ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoTooltip;
6137
0
        ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2));
6138
0
        if (ref_col != NULL)
6139
0
        {
6140
0
            Text("Original");
6141
0
            ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);
6142
0
            if (ColorButton("##original", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)))
6143
0
            {
6144
0
                memcpy(col, ref_col, components * sizeof(float));
6145
0
                value_changed = true;
6146
0
            }
6147
0
        }
6148
0
        PopItemFlag();
6149
0
        EndGroup();
6150
0
    }
6151
6152
    // Convert back color to RGB
6153
0
    if (value_changed_h || value_changed_sv)
6154
0
    {
6155
0
        if (flags & ImGuiColorEditFlags_InputRGB)
6156
0
        {
6157
0
            ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]);
6158
0
            g.ColorEditSavedHue = H;
6159
0
            g.ColorEditSavedSat = S;
6160
0
            g.ColorEditSavedID = g.ColorEditCurrentID;
6161
0
            g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));
6162
0
        }
6163
0
        else if (flags & ImGuiColorEditFlags_InputHSV)
6164
0
        {
6165
0
            col[0] = H;
6166
0
            col[1] = S;
6167
0
            col[2] = V;
6168
0
        }
6169
0
    }
6170
6171
    // R,G,B and H,S,V slider color editor
6172
0
    bool value_changed_fix_hue_wrap = false;
6173
0
    if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
6174
0
    {
6175
0
        PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
6176
0
        ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview;
6177
0
        ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
6178
0
        if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
6179
0
            if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))
6180
0
            {
6181
                // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.
6182
                // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050)
6183
0
                value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);
6184
0
                value_changed = true;
6185
0
            }
6186
0
        if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
6187
0
            value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_DisplayHSV);
6188
0
        if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
6189
0
            value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_DisplayHex);
6190
0
        PopItemWidth();
6191
0
    }
6192
6193
    // Try to cancel hue wrap (after ColorEdit4 call), if any
6194
0
    if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB))
6195
0
    {
6196
0
        float new_H, new_S, new_V;
6197
0
        ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);
6198
0
        if (new_H <= 0 && H > 0)
6199
0
        {
6200
0
            if (new_V <= 0 && V != new_V)
6201
0
                ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);
6202
0
            else if (new_S <= 0)
6203
0
                ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);
6204
0
        }
6205
0
    }
6206
6207
0
    if (value_changed)
6208
0
    {
6209
0
        if (flags & ImGuiColorEditFlags_InputRGB)
6210
0
        {
6211
0
            R = col[0];
6212
0
            G = col[1];
6213
0
            B = col[2];
6214
0
            ColorConvertRGBtoHSV(R, G, B, H, S, V);
6215
0
            ColorEditRestoreHS(col, &H, &S, &V);   // Fix local Hue as display below will use it immediately.
6216
0
        }
6217
0
        else if (flags & ImGuiColorEditFlags_InputHSV)
6218
0
        {
6219
0
            H = col[0];
6220
0
            S = col[1];
6221
0
            V = col[2];
6222
0
            ColorConvertHSVtoRGB(H, S, V, R, G, B);
6223
0
        }
6224
0
    }
6225
6226
0
    const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha);
6227
0
    const ImU32 col_black = IM_COL32(0,0,0,style_alpha8);
6228
0
    const ImU32 col_white = IM_COL32(255,255,255,style_alpha8);
6229
0
    const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8);
6230
0
    const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) };
6231
6232
0
    ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);
6233
0
    ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);
6234
0
    ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!!
6235
6236
0
    ImVec2 sv_cursor_pos;
6237
6238
0
    if (flags & ImGuiColorEditFlags_PickerHueWheel)
6239
0
    {
6240
        // Render Hue Wheel
6241
0
        const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).
6242
0
        const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);
6243
0
        for (int n = 0; n < 6; n++)
6244
0
        {
6245
0
            const float a0 = (n)     /6.0f * 2.0f * IM_PI - aeps;
6246
0
            const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;
6247
0
            const int vert_start_idx = draw_list->VtxBuffer.Size;
6248
0
            draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);
6249
0
            draw_list->PathStroke(col_white, 0, wheel_thickness);
6250
0
            const int vert_end_idx = draw_list->VtxBuffer.Size;
6251
6252
            // Paint colors over existing vertices
6253
0
            ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);
6254
0
            ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);
6255
0
            ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]);
6256
0
        }
6257
6258
        // Render Cursor + preview on Hue Wheel
6259
0
        float cos_hue_angle = ImCos(H * 2.0f * IM_PI);
6260
0
        float sin_hue_angle = ImSin(H * 2.0f * IM_PI);
6261
0
        ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f);
6262
0
        float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;
6263
0
        int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others.
6264
0
        draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);
6265
0
        draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments);
6266
0
        draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments);
6267
6268
        // Render SV triangle (rotated according to hue)
6269
0
        ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);
6270
0
        ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);
6271
0
        ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);
6272
0
        ImVec2 uv_white = GetFontTexUvWhitePixel();
6273
0
        draw_list->PrimReserve(3, 3);
6274
0
        draw_list->PrimVtx(tra, uv_white, hue_color32);
6275
0
        draw_list->PrimVtx(trb, uv_white, col_black);
6276
0
        draw_list->PrimVtx(trc, uv_white, col_white);
6277
0
        draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f);
6278
0
        sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));
6279
0
    }
6280
0
    else if (flags & ImGuiColorEditFlags_PickerHueBar)
6281
0
    {
6282
        // Render SV Square
6283
0
        draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white);
6284
0
        draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black);
6285
0
        RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f);
6286
0
        sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S)     * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much
6287
0
        sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);
6288
6289
        // Render Hue Bar
6290
0
        for (int i = 0; i < 6; ++i)
6291
0
            draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]);
6292
0
        float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size);
6293
0
        RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);
6294
0
        RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha);
6295
0
    }
6296
6297
    // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)
6298
0
    float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f;
6299
0
    int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others.
6300
0
    draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments);
6301
0
    draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments);
6302
0
    draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments);
6303
6304
    // Render alpha bar
6305
0
    if (alpha_bar)
6306
0
    {
6307
0
        float alpha = ImSaturate(col[3]);
6308
0
        ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);
6309
0
        RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));
6310
0
        draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK);
6311
0
        float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size);
6312
0
        RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);
6313
0
        RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha);
6314
0
    }
6315
6316
0
    EndGroup();
6317
6318
0
    if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)
6319
0
        value_changed = false;
6320
0
    if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId
6321
0
        MarkItemEdited(g.LastItemData.ID);
6322
6323
0
    if (set_current_color_edit_id)
6324
0
        g.ColorEditCurrentID = 0;
6325
0
    PopID();
6326
6327
0
    return value_changed;
6328
0
}
6329
6330
// A little color square. Return true when clicked.
6331
// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.
6332
// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.
6333
// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set.
6334
bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg)
6335
0
{
6336
0
    ImGuiWindow* window = GetCurrentWindow();
6337
0
    if (window->SkipItems)
6338
0
        return false;
6339
6340
0
    ImGuiContext& g = *GImGui;
6341
0
    const ImGuiID id = window->GetID(desc_id);
6342
0
    const float default_size = GetFrameHeight();
6343
0
    const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y);
6344
0
    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
6345
0
    ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);
6346
0
    if (!ItemAdd(bb, id))
6347
0
        return false;
6348
6349
0
    bool hovered, held;
6350
0
    bool pressed = ButtonBehavior(bb, id, &hovered, &held);
6351
6352
0
    if (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque))
6353
0
        flags &= ~(ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf);
6354
6355
0
    ImVec4 col_rgb = col;
6356
0
    if (flags & ImGuiColorEditFlags_InputHSV)
6357
0
        ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z);
6358
6359
0
    ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f);
6360
0
    float grid_step = ImMin(size.x, size.y) / 2.99f;
6361
0
    float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);
6362
0
    ImRect bb_inner = bb;
6363
0
    float off = 0.0f;
6364
0
    if ((flags & ImGuiColorEditFlags_NoBorder) == 0)
6365
0
    {
6366
0
        off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.
6367
0
        bb_inner.Expand(off);
6368
0
    }
6369
0
    if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f)
6370
0
    {
6371
0
        float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f);
6372
0
        if ((flags & ImGuiColorEditFlags_AlphaNoBg) == 0)
6373
0
            RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight);
6374
0
        else
6375
0
            window->DrawList->AddRectFilled(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), rounding, ImDrawFlags_RoundCornersRight);
6376
0
        window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft);
6377
0
    }
6378
0
    else
6379
0
    {
6380
        // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha
6381
0
        ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaOpaque) ? col_rgb_without_alpha : col_rgb;
6382
0
        if (col_source.w < 1.0f && (flags & ImGuiColorEditFlags_AlphaNoBg) == 0)
6383
0
            RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);
6384
0
        else
6385
0
            window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding);
6386
0
    }
6387
0
    RenderNavCursor(bb, id);
6388
0
    if ((flags & ImGuiColorEditFlags_NoBorder) == 0)
6389
0
    {
6390
0
        if (g.Style.FrameBorderSize > 0.0f)
6391
0
            RenderFrameBorder(bb.Min, bb.Max, rounding);
6392
0
        else
6393
0
            window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color buttons are often in need of some sort of border // FIXME-DPI
6394
0
    }
6395
6396
    // Drag and Drop Source
6397
    // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.
6398
0
    if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())
6399
0
    {
6400
0
        if (flags & ImGuiColorEditFlags_NoAlpha)
6401
0
            SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once);
6402
0
        else
6403
0
            SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once);
6404
0
        ColorButton(desc_id, col, flags);
6405
0
        SameLine();
6406
0
        TextEx("Color");
6407
0
        EndDragDropSource();
6408
0
    }
6409
6410
    // Tooltip
6411
0
    if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip))
6412
0
        ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_));
6413
6414
0
    return pressed;
6415
0
}
6416
6417
// Initialize/override default color options
6418
void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)
6419
0
{
6420
0
    ImGuiContext& g = *GImGui;
6421
0
    if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0)
6422
0
        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_;
6423
0
    if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0)
6424
0
        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_;
6425
0
    if ((flags & ImGuiColorEditFlags_PickerMask_) == 0)
6426
0
        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_;
6427
0
    if ((flags & ImGuiColorEditFlags_InputMask_) == 0)
6428
0
        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_;
6429
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_));    // Check only 1 option is selected
6430
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_));   // Check only 1 option is selected
6431
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_));     // Check only 1 option is selected
6432
0
    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));      // Check only 1 option is selected
6433
0
    g.ColorEditOptions = flags;
6434
0
}
6435
6436
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
6437
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
6438
0
{
6439
0
    ImGuiContext& g = *GImGui;
6440
6441
0
    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
6442
0
        return;
6443
0
    const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;
6444
0
    if (text_end > text)
6445
0
    {
6446
0
        TextEx(text, text_end);
6447
0
        Separator();
6448
0
    }
6449
6450
0
    ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);
6451
0
    ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
6452
0
    int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
6453
0
    ImGuiColorEditFlags flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_;
6454
0
    ColorButton("##preview", cf, (flags & flags_to_forward) | ImGuiColorEditFlags_NoTooltip, sz);
6455
0
    SameLine();
6456
0
    if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_))
6457
0
    {
6458
0
        if (flags & ImGuiColorEditFlags_NoAlpha)
6459
0
            Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);
6460
0
        else
6461
0
            Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);
6462
0
    }
6463
0
    else if (flags & ImGuiColorEditFlags_InputHSV)
6464
0
    {
6465
0
        if (flags & ImGuiColorEditFlags_NoAlpha)
6466
0
            Text("H: %.3f, S: %.3f, V: %.3f", col[0], col[1], col[2]);
6467
0
        else
6468
0
            Text("H: %.3f, S: %.3f, V: %.3f, A: %.3f", col[0], col[1], col[2], col[3]);
6469
0
    }
6470
0
    EndTooltip();
6471
0
}
6472
6473
void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)
6474
0
{
6475
0
    bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_);
6476
0
    bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_);
6477
0
    if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context"))
6478
0
        return;
6479
6480
0
    ImGuiContext& g = *GImGui;
6481
0
    PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);
6482
0
    ImGuiColorEditFlags opts = g.ColorEditOptions;
6483
0
    if (allow_opt_inputs)
6484
0
    {
6485
0
        if (RadioButton("RGB", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB;
6486
0
        if (RadioButton("HSV", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV;
6487
0
        if (RadioButton("Hex", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex;
6488
0
    }
6489
0
    if (allow_opt_datatype)
6490
0
    {
6491
0
        if (allow_opt_inputs) Separator();
6492
0
        if (RadioButton("0..255",     (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8;
6493
0
        if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float;
6494
0
    }
6495
6496
0
    if (allow_opt_inputs || allow_opt_datatype)
6497
0
        Separator();
6498
0
    if (Button("Copy as..", ImVec2(-1, 0)))
6499
0
        OpenPopup("Copy");
6500
0
    if (BeginPopup("Copy"))
6501
0
    {
6502
0
        int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);
6503
0
        char buf[64];
6504
0
        ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);
6505
0
        if (Selectable(buf))
6506
0
            SetClipboardText(buf);
6507
0
        ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca);
6508
0
        if (Selectable(buf))
6509
0
            SetClipboardText(buf);
6510
0
        ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb);
6511
0
        if (Selectable(buf))
6512
0
            SetClipboardText(buf);
6513
0
        if (!(flags & ImGuiColorEditFlags_NoAlpha))
6514
0
        {
6515
0
            ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", cr, cg, cb, ca);
6516
0
            if (Selectable(buf))
6517
0
                SetClipboardText(buf);
6518
0
        }
6519
0
        EndPopup();
6520
0
    }
6521
6522
0
    g.ColorEditOptions = opts;
6523
0
    PopItemFlag();
6524
0
    EndPopup();
6525
0
}
6526
6527
void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)
6528
0
{
6529
0
    bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_);
6530
0
    bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);
6531
0
    if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context"))
6532
0
        return;
6533
6534
0
    ImGuiContext& g = *GImGui;
6535
0
    PushItemFlag(ImGuiItemFlags_NoMarkEdited, true);
6536
0
    if (allow_opt_picker)
6537
0
    {
6538
0
        ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function
6539
0
        PushItemWidth(picker_size.x);
6540
0
        for (int picker_type = 0; picker_type < 2; picker_type++)
6541
0
        {
6542
            // Draw small/thumbnail version of each picker type (over an invisible button for selection)
6543
0
            if (picker_type > 0) Separator();
6544
0
            PushID(picker_type);
6545
0
            ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha);
6546
0
            if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;
6547
0
            if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;
6548
0
            ImVec2 backup_pos = GetCursorScreenPos();
6549
0
            if (Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup
6550
0
                g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);
6551
0
            SetCursorScreenPos(backup_pos);
6552
0
            ImVec4 previewing_ref_col;
6553
0
            memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));
6554
0
            ColorPicker4("##previewing_picker", &previewing_ref_col.x, picker_flags);
6555
0
            PopID();
6556
0
        }
6557
0
        PopItemWidth();
6558
0
    }
6559
0
    if (allow_opt_alpha_bar)
6560
0
    {
6561
0
        if (allow_opt_picker) Separator();
6562
0
        CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);
6563
0
    }
6564
0
    PopItemFlag();
6565
0
    EndPopup();
6566
0
}
6567
6568
//-------------------------------------------------------------------------
6569
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
6570
//-------------------------------------------------------------------------
6571
// - TreeNode()
6572
// - TreeNodeV()
6573
// - TreeNodeEx()
6574
// - TreeNodeExV()
6575
// - TreeNodeStoreStackData() [Internal]
6576
// - TreeNodeBehavior() [Internal]
6577
// - TreePush()
6578
// - TreePop()
6579
// - GetTreeNodeToLabelSpacing()
6580
// - SetNextItemOpen()
6581
// - CollapsingHeader()
6582
//-------------------------------------------------------------------------
6583
6584
bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
6585
0
{
6586
0
    va_list args;
6587
0
    va_start(args, fmt);
6588
0
    bool is_open = TreeNodeExV(str_id, 0, fmt, args);
6589
0
    va_end(args);
6590
0
    return is_open;
6591
0
}
6592
6593
bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
6594
0
{
6595
0
    va_list args;
6596
0
    va_start(args, fmt);
6597
0
    bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
6598
0
    va_end(args);
6599
0
    return is_open;
6600
0
}
6601
6602
bool ImGui::TreeNode(const char* label)
6603
0
{
6604
0
    ImGuiWindow* window = GetCurrentWindow();
6605
0
    if (window->SkipItems)
6606
0
        return false;
6607
0
    ImGuiID id = window->GetID(label);
6608
0
    return TreeNodeBehavior(id, ImGuiTreeNodeFlags_None, label, NULL);
6609
0
}
6610
6611
bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
6612
0
{
6613
0
    return TreeNodeExV(str_id, 0, fmt, args);
6614
0
}
6615
6616
bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
6617
0
{
6618
0
    return TreeNodeExV(ptr_id, 0, fmt, args);
6619
0
}
6620
6621
bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
6622
0
{
6623
0
    ImGuiWindow* window = GetCurrentWindow();
6624
0
    if (window->SkipItems)
6625
0
        return false;
6626
0
    ImGuiID id = window->GetID(label);
6627
0
    return TreeNodeBehavior(id, flags, label, NULL);
6628
0
}
6629
6630
bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6631
0
{
6632
0
    va_list args;
6633
0
    va_start(args, fmt);
6634
0
    bool is_open = TreeNodeExV(str_id, flags, fmt, args);
6635
0
    va_end(args);
6636
0
    return is_open;
6637
0
}
6638
6639
bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
6640
0
{
6641
0
    va_list args;
6642
0
    va_start(args, fmt);
6643
0
    bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
6644
0
    va_end(args);
6645
0
    return is_open;
6646
0
}
6647
6648
bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6649
0
{
6650
0
    ImGuiWindow* window = GetCurrentWindow();
6651
0
    if (window->SkipItems)
6652
0
        return false;
6653
6654
0
    ImGuiID id = window->GetID(str_id);
6655
0
    const char* label, *label_end;
6656
0
    ImFormatStringToTempBufferV(&label, &label_end, fmt, args);
6657
0
    return TreeNodeBehavior(id, flags, label, label_end);
6658
0
}
6659
6660
bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
6661
0
{
6662
0
    ImGuiWindow* window = GetCurrentWindow();
6663
0
    if (window->SkipItems)
6664
0
        return false;
6665
6666
0
    ImGuiID id = window->GetID(ptr_id);
6667
0
    const char* label, *label_end;
6668
0
    ImFormatStringToTempBufferV(&label, &label_end, fmt, args);
6669
0
    return TreeNodeBehavior(id, flags, label, label_end);
6670
0
}
6671
6672
bool ImGui::TreeNodeGetOpen(ImGuiID storage_id)
6673
0
{
6674
0
    ImGuiContext& g = *GImGui;
6675
0
    ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage;
6676
0
    return storage->GetInt(storage_id, 0) != 0;
6677
0
}
6678
6679
void ImGui::TreeNodeSetOpen(ImGuiID storage_id, bool open)
6680
0
{
6681
0
    ImGuiContext& g = *GImGui;
6682
0
    ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage;
6683
0
    storage->SetInt(storage_id, open ? 1 : 0);
6684
0
}
6685
6686
bool ImGui::TreeNodeUpdateNextOpen(ImGuiID storage_id, ImGuiTreeNodeFlags flags)
6687
0
{
6688
0
    if (flags & ImGuiTreeNodeFlags_Leaf)
6689
0
        return true;
6690
6691
    // We only write to the tree storage if the user clicks, or explicitly use the SetNextItemOpen function
6692
0
    ImGuiContext& g = *GImGui;
6693
0
    ImGuiWindow* window = g.CurrentWindow;
6694
0
    ImGuiStorage* storage = window->DC.StateStorage;
6695
6696
0
    bool is_open;
6697
0
    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasOpen)
6698
0
    {
6699
0
        if (g.NextItemData.OpenCond & ImGuiCond_Always)
6700
0
        {
6701
0
            is_open = g.NextItemData.OpenVal;
6702
0
            TreeNodeSetOpen(storage_id, is_open);
6703
0
        }
6704
0
        else
6705
0
        {
6706
            // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
6707
0
            const int stored_value = storage->GetInt(storage_id, -1);
6708
0
            if (stored_value == -1)
6709
0
            {
6710
0
                is_open = g.NextItemData.OpenVal;
6711
0
                TreeNodeSetOpen(storage_id, is_open);
6712
0
            }
6713
0
            else
6714
0
            {
6715
0
                is_open = stored_value != 0;
6716
0
            }
6717
0
        }
6718
0
    }
6719
0
    else
6720
0
    {
6721
0
        is_open = storage->GetInt(storage_id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
6722
0
    }
6723
6724
    // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).
6725
    // NB- If we are above max depth we still allow manually opened nodes to be logged.
6726
0
    if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand)
6727
0
        is_open = true;
6728
6729
0
    return is_open;
6730
0
}
6731
6732
// Store ImGuiTreeNodeStackData for just submitted node.
6733
// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.
6734
static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags, float x1)
6735
0
{
6736
0
    ImGuiContext& g = *GImGui;
6737
0
    ImGuiWindow* window = g.CurrentWindow;
6738
6739
0
    g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1);
6740
0
    ImGuiTreeNodeStackData* tree_node_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
6741
0
    tree_node_data->ID = g.LastItemData.ID;
6742
0
    tree_node_data->TreeFlags = flags;
6743
0
    tree_node_data->ItemFlags = g.LastItemData.ItemFlags;
6744
0
    tree_node_data->NavRect = g.LastItemData.NavRect;
6745
6746
    // Initially I tried to latch value for GetColorU32(ImGuiCol_TreeLines) but it's not a good trade-off for very large trees.
6747
0
    const bool draw_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) != 0;
6748
0
    tree_node_data->DrawLinesX1 = draw_lines ? (x1 + g.FontSize * 0.5f + g.Style.FramePadding.x) : +FLT_MAX;
6749
0
    tree_node_data->DrawLinesTableColumn = (draw_lines && g.CurrentTable) ? (ImGuiTableColumnIdx)g.CurrentTable->CurrentColumn : -1;
6750
0
    tree_node_data->DrawLinesToNodesY2 = -FLT_MAX;
6751
0
    window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth);
6752
0
    if (flags & ImGuiTreeNodeFlags_DrawLinesToNodes)
6753
0
        window->DC.TreeRecordsClippedNodesY2Mask |= (1 << window->DC.TreeDepth);
6754
0
}
6755
6756
// When using public API, currently 'id == storage_id' is always true, but we separate the values to facilitate advanced user code doing storage queries outside of UI loop.
6757
bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
6758
0
{
6759
0
    ImGuiWindow* window = GetCurrentWindow();
6760
0
    if (window->SkipItems)
6761
0
        return false;
6762
6763
0
    ImGuiContext& g = *GImGui;
6764
0
    const ImGuiStyle& style = g.Style;
6765
0
    const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
6766
0
    const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y));
6767
6768
0
    if (!label_end)
6769
0
        label_end = FindRenderedTextEnd(label);
6770
0
    const ImVec2 label_size = CalcTextSize(label, label_end, false);
6771
6772
0
    const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2);   // Collapsing arrow width + Spacing
6773
0
    const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset);            // Latch before ItemSize changes it
6774
0
    const float text_width = g.FontSize + label_size.x + padding.x * 2;                         // Include collapsing arrow
6775
6776
    // We vertically grow up to current line height up the typical widget height.
6777
0
    const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);
6778
0
    const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL);
6779
0
    const bool span_all_columns_label = (flags & ImGuiTreeNodeFlags_LabelSpanAllColumns) != 0 && (g.CurrentTable != NULL);
6780
0
    ImRect frame_bb;
6781
0
    frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
6782
0
    frame_bb.Min.y = window->DC.CursorPos.y;
6783
0
    frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : (flags & ImGuiTreeNodeFlags_SpanLabelWidth) ? window->DC.CursorPos.x + text_width + padding.x : window->WorkRect.Max.x;
6784
0
    frame_bb.Max.y = window->DC.CursorPos.y + frame_height;
6785
0
    if (display_frame)
6786
0
    {
6787
0
        const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits
6788
0
        frame_bb.Min.x -= outer_extend;
6789
0
        frame_bb.Max.x += outer_extend;
6790
0
    }
6791
6792
0
    ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
6793
0
    ItemSize(ImVec2(text_width, frame_height), padding.y);
6794
6795
    // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
6796
0
    ImRect interact_bb = frame_bb;
6797
0
    if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanLabelWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)
6798
0
        interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f);
6799
6800
    // Compute open and multi-select states before ItemAdd() as it clear NextItem data.
6801
0
    ImGuiID storage_id = (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasStorageID) ? g.NextItemData.StorageId : id;
6802
0
    bool is_open = TreeNodeUpdateNextOpen(storage_id, flags);
6803
6804
0
    bool is_visible;
6805
0
    if (span_all_columns || span_all_columns_label)
6806
0
    {
6807
        // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
6808
0
        const float backup_clip_rect_min_x = window->ClipRect.Min.x;
6809
0
        const float backup_clip_rect_max_x = window->ClipRect.Max.x;
6810
0
        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
6811
0
        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
6812
0
        is_visible = ItemAdd(interact_bb, id);
6813
0
        window->ClipRect.Min.x = backup_clip_rect_min_x;
6814
0
        window->ClipRect.Max.x = backup_clip_rect_max_x;
6815
0
    }
6816
0
    else
6817
0
    {
6818
0
        is_visible = ItemAdd(interact_bb, id);
6819
0
    }
6820
0
    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
6821
0
    g.LastItemData.DisplayRect = frame_bb;
6822
6823
    // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsToParent enabled:
6824
    // Store data for the current depth to allow returning to this node from any child item.
6825
    // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
6826
    // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsToParent by default or move it to ImGuiStyle.
6827
0
    bool store_tree_node_stack_data = false;
6828
0
    if ((flags & ImGuiTreeNodeFlags_DrawLinesMask_) == 0)
6829
0
        flags |= g.Style.TreeLinesFlags;
6830
0
    const bool draw_tree_lines = (flags & (ImGuiTreeNodeFlags_DrawLinesFull | ImGuiTreeNodeFlags_DrawLinesToNodes)) && (frame_bb.Min.y < window->ClipRect.Max.y) && (g.Style.TreeLinesSize > 0.0f);
6831
0
    if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6832
0
    {
6833
0
        store_tree_node_stack_data = draw_tree_lines;
6834
0
        if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsToParent) && !g.NavIdIsAlive)
6835
0
            if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
6836
0
                store_tree_node_stack_data = true;
6837
0
    }
6838
6839
0
    const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
6840
0
    if (!is_visible)
6841
0
    {
6842
0
        if ((flags & ImGuiTreeNodeFlags_DrawLinesToNodes) && (window->DC.TreeRecordsClippedNodesY2Mask & (1 << (window->DC.TreeDepth - 1))))
6843
0
        {
6844
0
            ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
6845
0
            parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, window->DC.CursorPos.y); // Don't need to aim to mid Y position as we are clipped anyway.
6846
0
            if (frame_bb.Min.y >= window->ClipRect.Max.y)
6847
0
                window->DC.TreeRecordsClippedNodesY2Mask &= ~(1 << (window->DC.TreeDepth - 1)); // Done
6848
0
        }
6849
0
        if (is_open && store_tree_node_stack_data)
6850
0
            TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
6851
0
        if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
6852
0
            TreePushOverrideID(id);
6853
0
        IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
6854
0
        return is_open;
6855
0
    }
6856
6857
0
    if (span_all_columns || span_all_columns_label)
6858
0
    {
6859
0
        TablePushBackgroundChannel();
6860
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
6861
0
        g.LastItemData.ClipRect = window->ClipRect;
6862
0
    }
6863
6864
0
    ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
6865
0
    if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap))
6866
0
        button_flags |= ImGuiButtonFlags_AllowOverlap;
6867
0
    if (!is_leaf)
6868
0
        button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
6869
6870
    // We allow clicking on the arrow section with keyboard modifiers held, in order to easily
6871
    // allow browsing a tree while preserving selection with code implementing multi-selection patterns.
6872
    // When clicking on the rest of the tree node we always disallow keyboard modifiers.
6873
0
    const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;
6874
0
    const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;
6875
0
    const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);
6876
6877
0
    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
6878
0
    if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes by default
6879
0
        flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick : ImGuiTreeNodeFlags_OpenOnArrow;
6880
6881
    // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.
6882
    // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.
6883
    // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0)
6884
    // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0)
6885
    // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1)
6886
    // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1)
6887
    // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0)
6888
    // It is rather standard that arrow click react on Down rather than Up.
6889
    // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work.
6890
0
    if (is_mouse_x_over_arrow)
6891
0
        button_flags |= ImGuiButtonFlags_PressedOnClick;
6892
0
    else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
6893
0
        button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
6894
0
    else
6895
0
        button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
6896
0
    if (flags & ImGuiTreeNodeFlags_NoNavFocus)
6897
0
        button_flags |= ImGuiButtonFlags_NoNavFocus;
6898
6899
0
    bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
6900
0
    const bool was_selected = selected;
6901
6902
    // Multi-selection support (header)
6903
0
    if (is_multi_select)
6904
0
    {
6905
        // Handle multi-select + alter button flags for it
6906
0
        MultiSelectItemHeader(id, &selected, &button_flags);
6907
0
        if (is_mouse_x_over_arrow)
6908
0
            button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;
6909
0
    }
6910
0
    else
6911
0
    {
6912
0
        if (window != g.HoveredWindow || !is_mouse_x_over_arrow)
6913
0
            button_flags |= ImGuiButtonFlags_NoKeyModsAllowed;
6914
0
    }
6915
6916
0
    bool hovered, held;
6917
0
    bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
6918
0
    bool toggled = false;
6919
0
    if (!is_leaf)
6920
0
    {
6921
0
        if (pressed && g.DragDropHoldJustPressedId != id)
6922
0
        {
6923
0
            if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || (g.NavActivateId == id && !is_multi_select))
6924
0
                toggled = true; // Single click
6925
0
            if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
6926
0
                toggled |= is_mouse_x_over_arrow && !g.NavHighlightItemUnderNav; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
6927
0
            if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2)
6928
0
                toggled = true; // Double click
6929
0
        }
6930
0
        else if (pressed && g.DragDropHoldJustPressedId == id)
6931
0
        {
6932
0
            IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold);
6933
0
            if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
6934
0
                toggled = true;
6935
0
            else
6936
0
                pressed = false; // Cancel press so it doesn't trigger selection.
6937
0
        }
6938
6939
0
        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open)
6940
0
        {
6941
0
            toggled = true;
6942
0
            NavClearPreferredPosForAxis(ImGuiAxis_X);
6943
0
            NavMoveRequestCancel();
6944
0
        }
6945
0
        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
6946
0
        {
6947
0
            toggled = true;
6948
0
            NavClearPreferredPosForAxis(ImGuiAxis_X);
6949
0
            NavMoveRequestCancel();
6950
0
        }
6951
6952
0
        if (toggled)
6953
0
        {
6954
0
            is_open = !is_open;
6955
0
            window->DC.StateStorage->SetInt(storage_id, is_open);
6956
0
            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen;
6957
0
        }
6958
0
    }
6959
6960
    // Multi-selection support (footer)
6961
0
    if (is_multi_select)
6962
0
    {
6963
0
        bool pressed_copy = pressed && !toggled;
6964
0
        MultiSelectItemFooter(id, &selected, &pressed_copy);
6965
0
        if (pressed)
6966
0
            SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb);
6967
0
    }
6968
6969
0
    if (selected != was_selected)
6970
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
6971
6972
    // Render
6973
0
    {
6974
0
        const ImU32 text_col = GetColorU32(ImGuiCol_Text);
6975
0
        ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact;
6976
0
        if (is_multi_select)
6977
0
            nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle
6978
0
        if (display_frame)
6979
0
        {
6980
            // Framed type
6981
0
            const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
6982
0
            RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
6983
0
            RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
6984
0
            if (span_all_columns && !span_all_columns_label)
6985
0
                TablePopBackgroundChannel();
6986
0
            if (flags & ImGuiTreeNodeFlags_Bullet)
6987
0
                RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
6988
0
            else if (!is_leaf)
6989
0
                RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);
6990
0
            else // Leaf without bullet, left-adjusted text
6991
0
                text_pos.x -= text_offset_x - padding.x;
6992
0
            if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)
6993
0
                frame_bb.Max.x -= g.FontSize + style.FramePadding.x;
6994
0
            if (g.LogEnabled)
6995
0
                LogSetNextTextDecoration("###", "###");
6996
0
        }
6997
0
        else
6998
0
        {
6999
            // Unframed typed for tree nodes
7000
0
            if (hovered || selected)
7001
0
            {
7002
0
                const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
7003
0
                RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
7004
0
            }
7005
0
            RenderNavCursor(frame_bb, id, nav_render_cursor_flags);
7006
0
            if (span_all_columns && !span_all_columns_label)
7007
0
                TablePopBackgroundChannel();
7008
0
            if (flags & ImGuiTreeNodeFlags_Bullet)
7009
0
                RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
7010
0
            else if (!is_leaf)
7011
0
                RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);
7012
0
            if (g.LogEnabled)
7013
0
                LogSetNextTextDecoration(">", NULL);
7014
0
        }
7015
7016
0
        if (draw_tree_lines)
7017
0
            TreeNodeDrawLineToChildNode(ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.5f));
7018
7019
        // Label
7020
0
        if (display_frame)
7021
0
            RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
7022
0
        else
7023
0
            RenderText(text_pos, label, label_end, false);
7024
7025
0
        if (span_all_columns_label)
7026
0
            TablePopBackgroundChannel();
7027
0
    }
7028
7029
0
    if (is_open && store_tree_node_stack_data)
7030
0
        TreeNodeStoreStackData(flags, text_pos.x - text_offset_x); // Call before TreePushOverrideID()
7031
0
    if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
7032
0
        TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice
7033
7034
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
7035
0
    return is_open;
7036
0
}
7037
7038
// Draw horizontal line from our parent node
7039
// This is only called for visible child nodes so we are not too fussy anymore about performances
7040
void ImGui::TreeNodeDrawLineToChildNode(const ImVec2& target_pos)
7041
0
{
7042
0
    ImGuiContext& g = *GImGui;
7043
0
    ImGuiWindow* window = g.CurrentWindow;
7044
0
    if (window->DC.TreeDepth == 0 || (window->DC.TreeHasStackDataDepthMask & (1 << (window->DC.TreeDepth - 1))) == 0)
7045
0
        return;
7046
7047
0
    ImGuiTreeNodeStackData* parent_data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
7048
0
    float x1 = ImTrunc(parent_data->DrawLinesX1);
7049
0
    float x2 = ImTrunc(target_pos.x - g.Style.ItemInnerSpacing.x);
7050
0
    float y = ImTrunc(target_pos.y);
7051
0
    float rounding = (g.Style.TreeLinesRounding > 0.0f) ? ImMin(x2 - x1, g.Style.TreeLinesRounding) : 0.0f;
7052
0
    parent_data->DrawLinesToNodesY2 = ImMax(parent_data->DrawLinesToNodesY2, y - rounding);
7053
0
    if (x1 >= x2)
7054
0
        return;
7055
0
    if (rounding > 0.0f)
7056
0
    {
7057
0
        x1 += 0.5f + rounding;
7058
0
        window->DrawList->PathArcToFast(ImVec2(x1, y - rounding), rounding, 6, 3);
7059
0
        if (x1 < x2)
7060
0
            window->DrawList->PathLineTo(ImVec2(x2, y));
7061
0
        window->DrawList->PathStroke(GetColorU32(ImGuiCol_TreeLines), ImDrawFlags_None, g.Style.TreeLinesSize);
7062
0
    }
7063
0
    else
7064
0
    {
7065
0
        window->DrawList->AddLine(ImVec2(x1, y), ImVec2(x2, y), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
7066
0
    }
7067
0
}
7068
7069
// Draw vertical line of the hierarchy
7070
void ImGui::TreeNodeDrawLineToTreePop(const ImGuiTreeNodeStackData* data)
7071
0
{
7072
0
    ImGuiContext& g = *GImGui;
7073
0
    ImGuiWindow* window = g.CurrentWindow;
7074
0
    float y1 = ImMax(data->NavRect.Max.y, window->ClipRect.Min.y);
7075
0
    float y2 = data->DrawLinesToNodesY2;
7076
0
    if (data->TreeFlags & ImGuiTreeNodeFlags_DrawLinesFull)
7077
0
    {
7078
0
        float y2_full = window->DC.CursorPos.y;
7079
0
        if (g.CurrentTable)
7080
0
            y2_full = ImMax(g.CurrentTable->RowPosY2, y2_full);
7081
0
        y2_full = ImTrunc(y2_full - g.Style.ItemSpacing.y - g.FontSize * 0.5f);
7082
0
        if (y2 + (g.Style.ItemSpacing.y + g.Style.TreeLinesRounding) < y2_full) // FIXME: threshold to use ToNodes Y2 instead of Full Y2 when close by ItemSpacing.y
7083
0
            y2 = y2_full;
7084
0
    }
7085
0
    y2 = ImMin(y2, window->ClipRect.Max.y);
7086
0
    if (y2 <= y1)
7087
0
        return;
7088
0
    float x = ImTrunc(data->DrawLinesX1);
7089
0
    if (data->DrawLinesTableColumn != -1)
7090
0
        TablePushColumnChannel(data->DrawLinesTableColumn);
7091
0
    window->DrawList->AddLine(ImVec2(x, y1), ImVec2(x, y2), GetColorU32(ImGuiCol_TreeLines), g.Style.TreeLinesSize);
7092
0
    if (data->DrawLinesTableColumn != -1)
7093
0
        TablePopColumnChannel();
7094
0
}
7095
7096
void ImGui::TreePush(const char* str_id)
7097
0
{
7098
0
    ImGuiWindow* window = GetCurrentWindow();
7099
0
    Indent();
7100
0
    window->DC.TreeDepth++;
7101
0
    PushID(str_id);
7102
0
}
7103
7104
void ImGui::TreePush(const void* ptr_id)
7105
0
{
7106
0
    ImGuiWindow* window = GetCurrentWindow();
7107
0
    Indent();
7108
0
    window->DC.TreeDepth++;
7109
0
    PushID(ptr_id);
7110
0
}
7111
7112
void ImGui::TreePushOverrideID(ImGuiID id)
7113
0
{
7114
0
    ImGuiContext& g = *GImGui;
7115
0
    ImGuiWindow* window = g.CurrentWindow;
7116
0
    Indent();
7117
0
    window->DC.TreeDepth++;
7118
0
    PushOverrideID(id);
7119
0
}
7120
7121
void ImGui::TreePop()
7122
0
{
7123
0
    ImGuiContext& g = *GImGui;
7124
0
    ImGuiWindow* window = g.CurrentWindow;
7125
0
    Unindent();
7126
7127
0
    window->DC.TreeDepth--;
7128
0
    ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);
7129
7130
0
    if (window->DC.TreeHasStackDataDepthMask & tree_depth_mask)
7131
0
    {
7132
0
        const ImGuiTreeNodeStackData* data = &g.TreeNodeStack.Data[g.TreeNodeStack.Size - 1];
7133
0
        IM_ASSERT(data->ID == window->IDStack.back());
7134
7135
        // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsToParent is enabled)
7136
0
        if (data->TreeFlags & ImGuiTreeNodeFlags_NavLeftJumpsToParent)
7137
0
            if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())
7138
0
                NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, data);
7139
7140
        // Draw hierarchy lines
7141
0
        if (data->DrawLinesX1 != +FLT_MAX && window->DC.CursorPos.y >= window->ClipRect.Min.y)
7142
0
            TreeNodeDrawLineToTreePop(data);
7143
7144
0
        g.TreeNodeStack.pop_back();
7145
0
        window->DC.TreeHasStackDataDepthMask &= ~tree_depth_mask;
7146
0
        window->DC.TreeRecordsClippedNodesY2Mask &= ~tree_depth_mask;
7147
0
    }
7148
7149
0
    IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
7150
0
    PopID();
7151
0
}
7152
7153
// Horizontal distance preceding label when using TreeNode() or Bullet()
7154
float ImGui::GetTreeNodeToLabelSpacing()
7155
0
{
7156
0
    ImGuiContext& g = *GImGui;
7157
0
    return g.FontSize + (g.Style.FramePadding.x * 2.0f);
7158
0
}
7159
7160
// Set next TreeNode/CollapsingHeader open state.
7161
void ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond)
7162
0
{
7163
0
    ImGuiContext& g = *GImGui;
7164
0
    if (g.CurrentWindow->SkipItems)
7165
0
        return;
7166
0
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasOpen;
7167
0
    g.NextItemData.OpenVal = is_open;
7168
0
    g.NextItemData.OpenCond = (ImU8)(cond ? cond : ImGuiCond_Always);
7169
0
}
7170
7171
// Set next TreeNode/CollapsingHeader storage id.
7172
void ImGui::SetNextItemStorageID(ImGuiID storage_id)
7173
0
{
7174
0
    ImGuiContext& g = *GImGui;
7175
0
    if (g.CurrentWindow->SkipItems)
7176
0
        return;
7177
0
    g.NextItemData.HasFlags |= ImGuiNextItemDataFlags_HasStorageID;
7178
0
    g.NextItemData.StorageId = storage_id;
7179
0
}
7180
7181
// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
7182
// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
7183
bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
7184
0
{
7185
0
    ImGuiWindow* window = GetCurrentWindow();
7186
0
    if (window->SkipItems)
7187
0
        return false;
7188
0
    ImGuiID id = window->GetID(label);
7189
0
    return TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
7190
0
}
7191
7192
// p_visible == NULL                        : regular collapsing header
7193
// p_visible != NULL && *p_visible == true  : show a small close button on the corner of the header, clicking the button will set *p_visible = false
7194
// p_visible != NULL && *p_visible == false : do not show the header at all
7195
// Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen.
7196
bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags)
7197
0
{
7198
0
    ImGuiWindow* window = GetCurrentWindow();
7199
0
    if (window->SkipItems)
7200
0
        return false;
7201
7202
0
    if (p_visible && !*p_visible)
7203
0
        return false;
7204
7205
0
    ImGuiID id = window->GetID(label);
7206
0
    flags |= ImGuiTreeNodeFlags_CollapsingHeader;
7207
0
    if (p_visible)
7208
0
        flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton;
7209
0
    bool is_open = TreeNodeBehavior(id, flags, label);
7210
0
    if (p_visible != NULL)
7211
0
    {
7212
        // Create a small overlapping close button
7213
        // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.
7214
        // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow.
7215
0
        ImGuiContext& g = *GImGui;
7216
0
        ImGuiLastItemData last_item_backup = g.LastItemData;
7217
0
        float button_size = g.FontSize;
7218
0
        float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size);
7219
0
        float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y;
7220
0
        ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id);
7221
0
        if (CloseButton(close_button_id, ImVec2(button_x, button_y)))
7222
0
            *p_visible = false;
7223
0
        g.LastItemData = last_item_backup;
7224
0
    }
7225
7226
0
    return is_open;
7227
0
}
7228
7229
//-------------------------------------------------------------------------
7230
// [SECTION] Widgets: Selectable
7231
//-------------------------------------------------------------------------
7232
// - Selectable()
7233
//-------------------------------------------------------------------------
7234
7235
// Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image.
7236
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
7237
// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags.
7238
// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.
7239
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
7240
243k
{
7241
243k
    ImGuiWindow* window = GetCurrentWindow();
7242
243k
    if (window->SkipItems)
7243
0
        return false;
7244
7245
243k
    ImGuiContext& g = *GImGui;
7246
243k
    const ImGuiStyle& style = g.Style;
7247
7248
    // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
7249
243k
    ImGuiID id = window->GetID(label);
7250
243k
    ImVec2 label_size = CalcTextSize(label, NULL, true);
7251
243k
    ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
7252
243k
    ImVec2 pos = window->DC.CursorPos;
7253
243k
    pos.y += window->DC.CurrLineTextBaseOffset;
7254
243k
    ItemSize(size, 0.0f);
7255
7256
    // Fill horizontal space
7257
    // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.
7258
243k
    const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
7259
243k
    const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
7260
243k
    const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
7261
243k
    if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))
7262
1.98k
        size.x = ImMax(label_size.x, max_x - min_x);
7263
7264
    // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
7265
    // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos.
7266
243k
    ImRect bb(min_x, pos.y, min_x + size.x, pos.y + size.y);
7267
243k
    if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)
7268
243k
    {
7269
243k
        const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
7270
243k
        const float spacing_y = style.ItemSpacing.y;
7271
243k
        const float spacing_L = IM_TRUNC(spacing_x * 0.50f);
7272
243k
        const float spacing_U = IM_TRUNC(spacing_y * 0.50f);
7273
243k
        bb.Min.x -= spacing_L;
7274
243k
        bb.Min.y -= spacing_U;
7275
243k
        bb.Max.x += (spacing_x - spacing_L);
7276
243k
        bb.Max.y += (spacing_y - spacing_U);
7277
243k
    }
7278
    //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
7279
7280
243k
    const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
7281
243k
    const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None;
7282
243k
    bool is_visible;
7283
243k
    if (span_all_columns)
7284
0
    {
7285
        // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
7286
0
        const float backup_clip_rect_min_x = window->ClipRect.Min.x;
7287
0
        const float backup_clip_rect_max_x = window->ClipRect.Max.x;
7288
0
        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
7289
0
        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
7290
0
        is_visible = ItemAdd(bb, id, NULL, extra_item_flags);
7291
0
        window->ClipRect.Min.x = backup_clip_rect_min_x;
7292
0
        window->ClipRect.Max.x = backup_clip_rect_max_x;
7293
0
    }
7294
243k
    else
7295
243k
    {
7296
243k
        is_visible = ItemAdd(bb, id, NULL, extra_item_flags);
7297
243k
    }
7298
7299
243k
    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
7300
243k
    if (!is_visible)
7301
2
        if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb)) // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd)
7302
2
            return false;
7303
7304
243k
    const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
7305
243k
    if (disabled_item && !disabled_global) // Only testing this as an optimization
7306
0
        BeginDisabled();
7307
7308
    // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
7309
    // which would be advantageous since most selectable are not selected.
7310
243k
    if (span_all_columns)
7311
0
    {
7312
0
        if (g.CurrentTable)
7313
0
            TablePushBackgroundChannel();
7314
0
        else if (window->DC.CurrentColumns)
7315
0
            PushColumnsBackground();
7316
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
7317
0
        g.LastItemData.ClipRect = window->ClipRect;
7318
0
    }
7319
7320
    // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
7321
243k
    ImGuiButtonFlags button_flags = 0;
7322
243k
    if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }
7323
243k
    if (flags & ImGuiSelectableFlags_NoSetKeyOwner)     { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; }
7324
243k
    if (flags & ImGuiSelectableFlags_SelectOnClick)     { button_flags |= ImGuiButtonFlags_PressedOnClick; }
7325
243k
    if (flags & ImGuiSelectableFlags_SelectOnRelease)   { button_flags |= ImGuiButtonFlags_PressedOnRelease; }
7326
243k
    if (flags & ImGuiSelectableFlags_AllowDoubleClick)  { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }
7327
243k
    if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }
7328
7329
    // Multi-selection support (header)
7330
243k
    const bool was_selected = selected;
7331
243k
    if (is_multi_select)
7332
0
    {
7333
        // Handle multi-select + alter button flags for it
7334
0
        MultiSelectItemHeader(id, &selected, &button_flags);
7335
0
    }
7336
7337
243k
    bool hovered, held;
7338
243k
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
7339
243k
    bool auto_selected = false;
7340
7341
    // Multi-selection support (footer)
7342
243k
    if (is_multi_select)
7343
0
    {
7344
0
        MultiSelectItemFooter(id, &selected, &pressed);
7345
0
    }
7346
243k
    else
7347
243k
    {
7348
        // Auto-select when moved into
7349
        // - This will be more fully fleshed in the range-select branch
7350
        // - This is not exposed as it won't nicely work with some user side handling of shift/control
7351
        // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
7352
        //   - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
7353
        //   - (2) usage will fail with clipped items
7354
        //   The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
7355
243k
        if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
7356
0
            if (g.NavJustMovedToId == id && (g.NavJustMovedToKeyMods & ImGuiMod_Ctrl) == 0)
7357
0
                selected = pressed = auto_selected = true;
7358
243k
    }
7359
7360
    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with keyboard/gamepad
7361
243k
    if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
7362
406
    {
7363
406
        if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
7364
406
        {
7365
406
            SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)
7366
406
            if (g.IO.ConfigNavCursorVisibleAuto)
7367
406
                g.NavCursorVisible = false;
7368
406
        }
7369
406
    }
7370
243k
    if (pressed)
7371
2
        MarkItemEdited(id);
7372
7373
243k
    if (selected != was_selected)
7374
0
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
7375
7376
    // Render
7377
243k
    if (is_visible)
7378
243k
    {
7379
243k
        const bool highlighted = hovered || (flags & ImGuiSelectableFlags_Highlight);
7380
243k
        if (highlighted || selected)
7381
1.58k
        {
7382
            // Between 1.91.0 and 1.91.4 we made selected Selectable use an arbitrary lerp between _Header and _HeaderHovered. Removed that now. (#8106)
7383
1.58k
            ImU32 col = GetColorU32((held && highlighted) ? ImGuiCol_HeaderActive : highlighted ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
7384
1.58k
            RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
7385
1.58k
        }
7386
243k
        if (g.NavId == id)
7387
661
        {
7388
661
            ImGuiNavRenderCursorFlags nav_render_cursor_flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;
7389
661
            if (is_multi_select)
7390
0
                nav_render_cursor_flags |= ImGuiNavRenderCursorFlags_AlwaysDraw; // Always show the nav rectangle
7391
661
            RenderNavCursor(bb, id, nav_render_cursor_flags);
7392
661
        }
7393
243k
    }
7394
7395
243k
    if (span_all_columns)
7396
0
    {
7397
0
        if (g.CurrentTable)
7398
0
            TablePopBackgroundChannel();
7399
0
        else if (window->DC.CurrentColumns)
7400
0
            PopColumnsBackground();
7401
0
    }
7402
7403
    // Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns.
7404
243k
    if (is_visible)
7405
243k
        RenderTextClipped(pos, ImVec2(ImMin(pos.x + size.x, window->WorkRect.Max.x), pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb);
7406
7407
    // Automatically close popups
7408
243k
    if (pressed && !auto_selected && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups))
7409
1
        CloseCurrentPopup();
7410
7411
243k
    if (disabled_item && !disabled_global)
7412
0
        EndDisabled();
7413
7414
    // Selectable() always returns a pressed state!
7415
    // Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve
7416
    // selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect().
7417
243k
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
7418
243k
    return pressed; //-V1020
7419
243k
}
7420
7421
bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
7422
0
{
7423
0
    if (Selectable(label, *p_selected, flags, size_arg))
7424
0
    {
7425
0
        *p_selected = !*p_selected;
7426
0
        return true;
7427
0
    }
7428
0
    return false;
7429
0
}
7430
7431
7432
//-------------------------------------------------------------------------
7433
// [SECTION] Widgets: Typing-Select support
7434
//-------------------------------------------------------------------------
7435
7436
// [Experimental] Currently not exposed in public API.
7437
// Consume character inputs and return search request, if any.
7438
// This would typically only be called on the focused window or location you want to grab inputs for, e.g.
7439
//   if (ImGui::IsWindowFocused(...))
7440
//       if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest())
7441
//           focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1);
7442
// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer).
7443
ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)
7444
0
{
7445
0
    ImGuiContext& g = *GImGui;
7446
0
    ImGuiTypingSelectState* data = &g.TypingSelectState;
7447
0
    ImGuiTypingSelectRequest* out_request = &data->Request;
7448
7449
    // Clear buffer
7450
0
    const float TYPING_SELECT_RESET_TIMER = 1.80f;          // FIXME: Potentially move to IO config.
7451
0
    const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times
7452
0
    if (data->SearchBuffer[0] != 0)
7453
0
    {
7454
0
        bool clear_buffer = false;
7455
0
        clear_buffer |= (g.NavFocusScopeId != data->FocusScope);
7456
0
        clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time);
7457
0
        clear_buffer |= g.NavAnyRequest;
7458
0
        clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere
7459
0
        clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter);
7460
0
        clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;
7461
        //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); }
7462
0
        if (clear_buffer)
7463
0
            data->Clear();
7464
0
    }
7465
7466
    // Append to buffer
7467
0
    const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
7468
0
    int buffer_len = (int)ImStrlen(data->SearchBuffer);
7469
0
    bool select_request = false;
7470
0
    for (ImWchar w : g.IO.InputQueueCharacters)
7471
0
    {
7472
0
        const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1);
7473
0
        if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks
7474
0
            continue;
7475
0
        char w_buf[5];
7476
0
        ImTextCharToUtf8(w_buf, (unsigned int)w);
7477
0
        if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)
7478
0
        {
7479
0
            select_request = true; // Same character: don't need to append to buffer.
7480
0
            continue;
7481
0
        }
7482
0
        if (data->SingleCharModeLock)
7483
0
        {
7484
0
            data->Clear(); // Different character: clear
7485
0
            buffer_len = 0;
7486
0
        }
7487
0
        memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append
7488
0
        buffer_len += w_len;
7489
0
        select_request = true;
7490
0
    }
7491
0
    g.IO.InputQueueCharacters.resize(0);
7492
7493
    // Handle backspace
7494
0
    if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, ImGuiInputFlags_Repeat))
7495
0
    {
7496
0
        char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);
7497
0
        *p = 0;
7498
0
        buffer_len = (int)(p - data->SearchBuffer);
7499
0
    }
7500
7501
    // Return request if any
7502
0
    if (buffer_len == 0)
7503
0
        return NULL;
7504
0
    if (select_request)
7505
0
    {
7506
0
        data->FocusScope = g.NavFocusScopeId;
7507
0
        data->LastRequestFrame = g.FrameCount;
7508
0
        data->LastRequestTime = (float)g.Time;
7509
0
    }
7510
0
    out_request->Flags = flags;
7511
0
    out_request->SearchBufferLen = buffer_len;
7512
0
    out_request->SearchBuffer = data->SearchBuffer;
7513
0
    out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount);
7514
0
    out_request->SingleCharMode = false;
7515
0
    out_request->SingleCharSize = 0;
7516
7517
    // Calculate if buffer contains the same character repeated.
7518
    // - This can be used to implement a special search mode on first character.
7519
    // - Performed on UTF-8 codepoint for correctness.
7520
    // - SingleCharMode is always set for first input character, because it usually leads to a "next".
7521
0
    if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode)
7522
0
    {
7523
0
        const char* buf_begin = out_request->SearchBuffer;
7524
0
        const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen;
7525
0
        const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end);
7526
0
        const char* p = buf_begin + c0_len;
7527
0
        for (; p < buf_end; p += c0_len)
7528
0
            if (memcmp(buf_begin, p, (size_t)c0_len) != 0)
7529
0
                break;
7530
0
        const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;
7531
0
        out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock);
7532
0
        out_request->SingleCharSize = (ImS8)c0_len;
7533
0
        data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode.
7534
0
    }
7535
7536
0
    return out_request;
7537
0
}
7538
7539
static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2)
7540
0
{
7541
0
    int match_len = 0;
7542
0
    while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++))
7543
0
        match_len++;
7544
0
    return match_len;
7545
0
}
7546
7547
// Default handler for finding a result for typing-select. You may implement your own.
7548
// You might want to display a tooltip to visualize the current request SearchBuffer
7549
// When SingleCharMode is set:
7550
// - it is better to NOT display a tooltip of other on-screen display indicator.
7551
// - the index of the currently focused item is required.
7552
//   if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.
7553
int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
7554
0
{
7555
0
    if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot.
7556
0
        return -1;
7557
0
    int idx = -1;
7558
0
    if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))
7559
0
        idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx);
7560
0
    else
7561
0
        idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);
7562
0
    if (idx != -1)
7563
0
        SetNavCursorVisibleAfterMove();
7564
0
    return idx;
7565
0
}
7566
7567
// Special handling when a single character is repeated: perform search on a single letter and goes to next.
7568
int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
7569
0
{
7570
    // FIXME: Assume selection user data is index. Would be extremely practical.
7571
    //if (nav_item_idx == -1)
7572
    //    nav_item_idx = (int)g.NavLastValidSelectionUserData;
7573
7574
0
    int first_match_idx = -1;
7575
0
    bool return_next_match = false;
7576
0
    for (int idx = 0; idx < items_count; idx++)
7577
0
    {
7578
0
        const char* item_name = get_item_name_func(user_data, idx);
7579
0
        if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize)
7580
0
            continue;
7581
0
        if (return_next_match)                           // Return next matching item after current item.
7582
0
            return idx;
7583
0
        if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value.
7584
0
            return idx;
7585
0
        if (first_match_idx == -1)                       // Record first match for wrapping.
7586
0
            first_match_idx = idx;
7587
0
        if (nav_item_idx == idx)                         // Record that we encountering nav_item so we can return next match.
7588
0
            return_next_match = true;
7589
0
    }
7590
0
    return first_match_idx; // First result
7591
0
}
7592
7593
int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data)
7594
0
{
7595
0
    int longest_match_idx = -1;
7596
0
    int longest_match_len = 0;
7597
0
    for (int idx = 0; idx < items_count; idx++)
7598
0
    {
7599
0
        const char* item_name = get_item_name_func(user_data, idx);
7600
0
        const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name);
7601
0
        if (match_len <= longest_match_len)
7602
0
            continue;
7603
0
        longest_match_idx = idx;
7604
0
        longest_match_len = match_len;
7605
0
        if (match_len == req->SearchBufferLen)
7606
0
            break;
7607
0
    }
7608
0
    return longest_match_idx;
7609
0
}
7610
7611
void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)
7612
0
{
7613
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
7614
0
    Text("SearchBuffer = \"%s\"", data->SearchBuffer);
7615
0
    Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock);
7616
0
    Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame);
7617
#else
7618
    IM_UNUSED(data);
7619
#endif
7620
0
}
7621
7622
//-------------------------------------------------------------------------
7623
// [SECTION] Widgets: Box-Select support
7624
// This has been extracted away from Multi-Select logic in the hope that it could eventually be used elsewhere, but hasn't been yet.
7625
//-------------------------------------------------------------------------
7626
// Extra logic in MultiSelectItemFooter() and ImGuiListClipper::Step()
7627
//-------------------------------------------------------------------------
7628
// - BoxSelectPreStartDrag() [Internal]
7629
// - BoxSelectActivateDrag() [Internal]
7630
// - BoxSelectDeactivateDrag() [Internal]
7631
// - BoxSelectScrollWithMouseDrag() [Internal]
7632
// - BeginBoxSelect() [Internal]
7633
// - EndBoxSelect() [Internal]
7634
//-------------------------------------------------------------------------
7635
7636
// Call on the initial click.
7637
static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_item)
7638
0
{
7639
0
    ImGuiContext& g = *GImGui;
7640
0
    ImGuiBoxSelectState* bs = &g.BoxSelectState;
7641
0
    bs->ID = id;
7642
0
    bs->IsStarting = true; // Consider starting box-select.
7643
0
    bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid);
7644
0
    bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid;
7645
0
    bs->KeyMods = g.IO.KeyMods;
7646
0
    bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos);
7647
0
    bs->ScrollAccum = ImVec2(0.0f, 0.0f);
7648
0
}
7649
7650
static void BoxSelectActivateDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window)
7651
0
{
7652
0
    ImGuiContext& g = *GImGui;
7653
0
    IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Activate\n", bs->ID);
7654
0
    bs->IsActive = true;
7655
0
    bs->Window = window;
7656
0
    bs->IsStarting = false;
7657
0
    ImGui::SetActiveID(bs->ID, window);
7658
0
    ImGui::SetActiveIdUsingAllKeyboardKeys();
7659
0
    if (bs->IsStartedFromVoid && (bs->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0)
7660
0
        bs->RequestClear = true;
7661
0
}
7662
7663
static void BoxSelectDeactivateDrag(ImGuiBoxSelectState* bs)
7664
0
{
7665
0
    ImGuiContext& g = *GImGui;
7666
0
    bs->IsActive = bs->IsStarting = false;
7667
0
    if (g.ActiveId == bs->ID)
7668
0
    {
7669
0
        IMGUI_DEBUG_LOG_SELECTION("[selection] BeginBoxSelect() 0X%08X: Deactivate\n", bs->ID);
7670
0
        ImGui::ClearActiveID();
7671
0
    }
7672
0
    bs->ID = 0;
7673
0
}
7674
7675
static void BoxSelectScrollWithMouseDrag(ImGuiBoxSelectState* bs, ImGuiWindow* window, const ImRect& inner_r)
7676
0
{
7677
0
    ImGuiContext& g = *GImGui;
7678
0
    IM_ASSERT(bs->Window == window);
7679
0
    for (int n = 0; n < 2; n++) // each axis
7680
0
    {
7681
0
        const float mouse_pos = g.IO.MousePos[n];
7682
0
        const float dist = (mouse_pos > inner_r.Max[n]) ? mouse_pos - inner_r.Max[n] : (mouse_pos < inner_r.Min[n]) ? mouse_pos - inner_r.Min[n] : 0.0f;
7683
0
        const float scroll_curr = window->Scroll[n];
7684
0
        if (dist == 0.0f || (dist < 0.0f && scroll_curr < 0.0f) || (dist > 0.0f && scroll_curr >= window->ScrollMax[n]))
7685
0
            continue;
7686
7687
0
        const float speed_multiplier = ImLinearRemapClamp(g.FontSize, g.FontSize * 5.0f, 1.0f, 4.0f, ImAbs(dist)); // x1 to x4 depending on distance
7688
0
        const float scroll_step = g.FontSize * 35.0f * speed_multiplier * ImSign(dist) * g.IO.DeltaTime;
7689
0
        bs->ScrollAccum[n] += scroll_step;
7690
7691
        // Accumulate into a stored value so we can handle high-framerate
7692
0
        const float scroll_step_i = ImFloor(bs->ScrollAccum[n]);
7693
0
        if (scroll_step_i == 0.0f)
7694
0
            continue;
7695
0
        if (n == 0)
7696
0
            ImGui::SetScrollX(window, scroll_curr + scroll_step_i);
7697
0
        else
7698
0
            ImGui::SetScrollY(window, scroll_curr + scroll_step_i);
7699
0
        bs->ScrollAccum[n] -= scroll_step_i;
7700
0
    }
7701
0
}
7702
7703
bool ImGui::BeginBoxSelect(const ImRect& scope_rect, ImGuiWindow* window, ImGuiID box_select_id, ImGuiMultiSelectFlags ms_flags)
7704
0
{
7705
0
    ImGuiContext& g = *GImGui;
7706
0
    ImGuiBoxSelectState* bs = &g.BoxSelectState;
7707
0
    KeepAliveID(box_select_id);
7708
0
    if (bs->ID != box_select_id)
7709
0
        return false;
7710
7711
    // IsStarting is set by MultiSelectItemFooter() when considering a possible box-select. We validate it here and lock geometry.
7712
0
    bs->UnclipMode = false;
7713
0
    bs->RequestClear = false;
7714
0
    if (bs->IsStarting && IsMouseDragPastThreshold(0))
7715
0
        BoxSelectActivateDrag(bs, window);
7716
0
    else if ((bs->IsStarting || bs->IsActive) && g.IO.MouseDown[0] == false)
7717
0
        BoxSelectDeactivateDrag(bs);
7718
0
    if (!bs->IsActive)
7719
0
        return false;
7720
7721
    // Current frame absolute prev/current rectangles are used to toggle selection.
7722
    // They are derived from positions relative to scrolling space.
7723
0
    ImVec2 start_pos_abs = WindowPosRelToAbs(window, bs->StartPosRel);
7724
0
    ImVec2 prev_end_pos_abs = WindowPosRelToAbs(window, bs->EndPosRel); // Clamped already
7725
0
    ImVec2 curr_end_pos_abs = g.IO.MousePos;
7726
0
    if (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) // Box-select scrolling only happens with ScopeWindow
7727
0
        curr_end_pos_abs = ImClamp(curr_end_pos_abs, scope_rect.Min, scope_rect.Max);
7728
0
    bs->BoxSelectRectPrev.Min = ImMin(start_pos_abs, prev_end_pos_abs);
7729
0
    bs->BoxSelectRectPrev.Max = ImMax(start_pos_abs, prev_end_pos_abs);
7730
0
    bs->BoxSelectRectCurr.Min = ImMin(start_pos_abs, curr_end_pos_abs);
7731
0
    bs->BoxSelectRectCurr.Max = ImMax(start_pos_abs, curr_end_pos_abs);
7732
7733
    // Box-select 2D mode detects horizontal changes (vertical ones are already picked by Clipper)
7734
    // Storing an extra rect used by widgets supporting box-select.
7735
0
    if (ms_flags & ImGuiMultiSelectFlags_BoxSelect2d)
7736
0
        if (bs->BoxSelectRectPrev.Min.x != bs->BoxSelectRectCurr.Min.x || bs->BoxSelectRectPrev.Max.x != bs->BoxSelectRectCurr.Max.x)
7737
0
        {
7738
0
            bs->UnclipMode = true;
7739
0
            bs->UnclipRect = bs->BoxSelectRectPrev; // FIXME-OPT: UnclipRect x coordinates could be intersection of Prev and Curr rect on X axis.
7740
0
            bs->UnclipRect.Add(bs->BoxSelectRectCurr);
7741
0
        }
7742
7743
    //GetForegroundDrawList()->AddRect(bs->UnclipRect.Min, bs->UnclipRect.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
7744
    //GetForegroundDrawList()->AddRect(bs->BoxSelectRectPrev.Min, bs->BoxSelectRectPrev.Max, IM_COL32(255,0,0,200), 0.0f, 0, 3.0f);
7745
    //GetForegroundDrawList()->AddRect(bs->BoxSelectRectCurr.Min, bs->BoxSelectRectCurr.Max, IM_COL32(0,255,0,200), 0.0f, 0, 1.0f);
7746
0
    return true;
7747
0
}
7748
7749
void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flags)
7750
0
{
7751
0
    ImGuiContext& g = *GImGui;
7752
0
    ImGuiWindow* window = g.CurrentWindow;
7753
0
    ImGuiBoxSelectState* bs = &g.BoxSelectState;
7754
0
    IM_ASSERT(bs->IsActive);
7755
0
    bs->UnclipMode = false;
7756
7757
    // Render selection rectangle
7758
0
    bs->EndPosRel = WindowPosAbsToRel(window, ImClamp(g.IO.MousePos, scope_rect.Min, scope_rect.Max)); // Clamp stored position according to current scrolling view
7759
0
    ImRect box_select_r = bs->BoxSelectRectCurr;
7760
0
    box_select_r.ClipWith(scope_rect);
7761
0
    window->DrawList->AddRectFilled(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_SeparatorHovered, 0.30f)); // FIXME-MULTISELECT: Styling
7762
0
    window->DrawList->AddRect(box_select_r.Min, box_select_r.Max, GetColorU32(ImGuiCol_NavCursor)); // FIXME-MULTISELECT FIXME-DPI: Styling
7763
7764
    // Scroll
7765
0
    const bool enable_scroll = (ms_flags & ImGuiMultiSelectFlags_ScopeWindow) && (ms_flags & ImGuiMultiSelectFlags_BoxSelectNoScroll) == 0;
7766
0
    if (enable_scroll)
7767
0
    {
7768
0
        ImRect scroll_r = scope_rect;
7769
0
        scroll_r.Expand(-g.FontSize);
7770
        //GetForegroundDrawList()->AddRect(scroll_r.Min, scroll_r.Max, IM_COL32(0, 255, 0, 255));
7771
0
        if (!scroll_r.Contains(g.IO.MousePos))
7772
0
            BoxSelectScrollWithMouseDrag(bs, window, scroll_r);
7773
0
    }
7774
0
}
7775
7776
//-------------------------------------------------------------------------
7777
// [SECTION] Widgets: Multi-Select support
7778
//-------------------------------------------------------------------------
7779
// - DebugLogMultiSelectRequests() [Internal]
7780
// - CalcScopeRect() [Internal]
7781
// - BeginMultiSelect()
7782
// - EndMultiSelect()
7783
// - SetNextItemSelectionUserData()
7784
// - MultiSelectItemHeader() [Internal]
7785
// - MultiSelectItemFooter() [Internal]
7786
// - DebugNodeMultiSelectState() [Internal]
7787
//-------------------------------------------------------------------------
7788
7789
static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io)
7790
0
{
7791
0
    ImGuiContext& g = *GImGui;
7792
0
    IM_UNUSED(function);
7793
0
    for (const ImGuiSelectionRequest& req : io->Requests)
7794
0
    {
7795
0
        if (req.Type == ImGuiSelectionRequestType_SetAll)    IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear");
7796
0
        if (req.Type == ImGuiSelectionRequestType_SetRange)  IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetRange %" IM_PRId64 "..%" IM_PRId64 " (0x%" IM_PRIX64 "..0x%" IM_PRIX64 ") = %d (dir %d)\n", function, req.RangeFirstItem, req.RangeLastItem, req.RangeFirstItem, req.RangeLastItem, req.Selected, req.RangeDirection);
7797
0
    }
7798
0
}
7799
7800
static ImRect CalcScopeRect(ImGuiMultiSelectTempData* ms, ImGuiWindow* window)
7801
0
{
7802
0
    ImGuiContext& g = *GImGui;
7803
0
    if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
7804
0
    {
7805
        // Warning: this depends on CursorMaxPos so it means to be called by EndMultiSelect() only
7806
0
        return ImRect(ms->ScopeRectMin, ImMax(window->DC.CursorMaxPos, ms->ScopeRectMin));
7807
0
    }
7808
0
    else
7809
0
    {
7810
        // When a table, pull HostClipRect, which allows us to predict ClipRect before first row/layout is performed. (#7970)
7811
0
        ImRect scope_rect = window->InnerClipRect;
7812
0
        if (g.CurrentTable != NULL)
7813
0
            scope_rect = g.CurrentTable->HostClipRect;
7814
7815
        // Add inner table decoration (#7821) // FIXME: Why not baking in InnerClipRect?
7816
0
        scope_rect.Min = ImMin(scope_rect.Min + ImVec2(window->DecoInnerSizeX1, window->DecoInnerSizeY1), scope_rect.Max);
7817
0
        return scope_rect;
7818
0
    }
7819
0
}
7820
7821
// Return ImGuiMultiSelectIO structure.
7822
// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().
7823
// Passing 'selection_size' and 'items_count' parameters is currently optional.
7824
// - 'selection_size' is useful to disable some shortcut routing: e.g. ImGuiMultiSelectFlags_ClearOnEscape won't claim Escape key when selection_size 0,
7825
//    allowing a first press to clear selection THEN the second press to leave child window and return to parent.
7826
// - 'items_count' is stored in ImGuiMultiSelectIO which makes it a convenient way to pass the information to your ApplyRequest() handler (but you may pass it differently).
7827
// - If they are costly for you to compute (e.g. external intrusive selection without maintaining size), you may avoid them and pass -1.
7828
//   - If you can easily tell if your selection is empty or not, you may pass 0/1, or you may enable ImGuiMultiSelectFlags_ClearOnEscape flag dynamically.
7829
ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int selection_size, int items_count)
7830
0
{
7831
0
    ImGuiContext& g = *GImGui;
7832
0
    ImGuiWindow* window = g.CurrentWindow;
7833
7834
0
    if (++g.MultiSelectTempDataStacked > g.MultiSelectTempData.Size)
7835
0
        g.MultiSelectTempData.resize(g.MultiSelectTempDataStacked, ImGuiMultiSelectTempData());
7836
0
    ImGuiMultiSelectTempData* ms = &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1];
7837
0
    IM_STATIC_ASSERT(offsetof(ImGuiMultiSelectTempData, IO) == 0); // Clear() relies on that.
7838
0
    g.CurrentMultiSelect = ms;
7839
0
    if ((flags & (ImGuiMultiSelectFlags_ScopeWindow | ImGuiMultiSelectFlags_ScopeRect)) == 0)
7840
0
        flags |= ImGuiMultiSelectFlags_ScopeWindow;
7841
0
    if (flags & ImGuiMultiSelectFlags_SingleSelect)
7842
0
        flags &= ~(ImGuiMultiSelectFlags_BoxSelect2d | ImGuiMultiSelectFlags_BoxSelect1d);
7843
0
    if (flags & ImGuiMultiSelectFlags_BoxSelect2d)
7844
0
        flags &= ~ImGuiMultiSelectFlags_BoxSelect1d;
7845
7846
    // FIXME: Workaround to the fact we override CursorMaxPos, meaning size measurement are lost. (#8250)
7847
    // They should perhaps be stacked properly?
7848
0
    if (ImGuiTable* table = g.CurrentTable)
7849
0
        if (table->CurrentColumn != -1)
7850
0
            TableEndCell(table); // This is currently safe to call multiple time. If that properly is lost we can extract the "save measurement" part of it.
7851
7852
    // FIXME: BeginFocusScope()
7853
0
    const ImGuiID id = window->IDStack.back();
7854
0
    ms->Clear();
7855
0
    ms->FocusScopeId = id;
7856
0
    ms->Flags = flags;
7857
0
    ms->IsFocused = (ms->FocusScopeId == g.NavFocusScopeId);
7858
0
    ms->BackupCursorMaxPos = window->DC.CursorMaxPos;
7859
0
    ms->ScopeRectMin = window->DC.CursorMaxPos = window->DC.CursorPos;
7860
0
    PushFocusScope(ms->FocusScopeId);
7861
0
    if (flags & ImGuiMultiSelectFlags_ScopeWindow) // Mark parent child window as navigable into, with highlight. Assume user will always submit interactive items.
7862
0
        window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main;
7863
7864
    // Use copy of keyboard mods at the time of the request, otherwise we would requires mods to be held for an extra frame.
7865
0
    ms->KeyMods = g.NavJustMovedToId ? (g.NavJustMovedToIsTabbing ? 0 : g.NavJustMovedToKeyMods) : g.IO.KeyMods;
7866
0
    if (flags & ImGuiMultiSelectFlags_NoRangeSelect)
7867
0
        ms->KeyMods &= ~ImGuiMod_Shift;
7868
7869
    // Bind storage
7870
0
    ImGuiMultiSelectState* storage = g.MultiSelectStorage.GetOrAddByKey(id);
7871
0
    storage->ID = id;
7872
0
    storage->LastFrameActive = g.FrameCount;
7873
0
    storage->LastSelectionSize = selection_size;
7874
0
    storage->Window = window;
7875
0
    ms->Storage = storage;
7876
7877
    // Output to user
7878
0
    ms->IO.Requests.resize(0);
7879
0
    ms->IO.RangeSrcItem = storage->RangeSrcItem;
7880
0
    ms->IO.NavIdItem = storage->NavIdItem;
7881
0
    ms->IO.NavIdSelected = (storage->NavIdSelected == 1) ? true : false;
7882
0
    ms->IO.ItemsCount = items_count;
7883
7884
    // Clear when using Navigation to move within the scope
7885
    // (we compare FocusScopeId so it possible to use multiple selections inside a same window)
7886
0
    bool request_clear = false;
7887
0
    bool request_select_all = false;
7888
0
    if (g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == ms->FocusScopeId && g.NavJustMovedToHasSelectionData)
7889
0
    {
7890
0
        if (ms->KeyMods & ImGuiMod_Shift)
7891
0
            ms->IsKeyboardSetRange = true;
7892
0
        if (ms->IsKeyboardSetRange)
7893
0
            IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid); // Not ready -> could clear?
7894
0
        if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0)
7895
0
            request_clear = true;
7896
0
    }
7897
0
    else if (g.NavJustMovedFromFocusScopeId == ms->FocusScopeId)
7898
0
    {
7899
        // Also clear on leaving scope (may be optional?)
7900
0
        if ((ms->KeyMods & (ImGuiMod_Ctrl | ImGuiMod_Shift)) == 0 && (flags & (ImGuiMultiSelectFlags_NoAutoClear | ImGuiMultiSelectFlags_NoAutoSelect)) == 0)
7901
0
            request_clear = true;
7902
0
    }
7903
7904
    // Box-select handling: update active state.
7905
0
    ImGuiBoxSelectState* bs = &g.BoxSelectState;
7906
0
    if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
7907
0
    {
7908
0
        ms->BoxSelectId = GetID("##BoxSelect");
7909
0
        if (BeginBoxSelect(CalcScopeRect(ms, window), window, ms->BoxSelectId, flags))
7910
0
            request_clear |= bs->RequestClear;
7911
0
    }
7912
7913
0
    if (ms->IsFocused)
7914
0
    {
7915
        // Shortcut: Clear selection (Escape)
7916
        // - Only claim shortcut if selection is not empty, allowing further presses on Escape to e.g. leave current child window.
7917
        // - Box select also handle Escape and needs to pass an id to bypass ActiveIdUsingAllKeyboardKeys lock.
7918
0
        if (flags & ImGuiMultiSelectFlags_ClearOnEscape)
7919
0
        {
7920
0
            if (selection_size != 0 || bs->IsActive)
7921
0
                if (Shortcut(ImGuiKey_Escape, ImGuiInputFlags_None, bs->IsActive ? bs->ID : 0))
7922
0
                {
7923
0
                    request_clear = true;
7924
0
                    if (bs->IsActive)
7925
0
                        BoxSelectDeactivateDrag(bs);
7926
0
                }
7927
0
        }
7928
7929
        // Shortcut: Select all (CTRL+A)
7930
0
        if (!(flags & ImGuiMultiSelectFlags_SingleSelect) && !(flags & ImGuiMultiSelectFlags_NoSelectAll))
7931
0
            if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_A))
7932
0
                request_select_all = true;
7933
0
    }
7934
7935
0
    if (request_clear || request_select_all)
7936
0
    {
7937
0
        MultiSelectAddSetAll(ms, request_select_all);
7938
0
        if (!request_select_all)
7939
0
            storage->LastSelectionSize = 0;
7940
0
    }
7941
0
    ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1;
7942
0
    ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid;
7943
7944
0
    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
7945
0
        DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO);
7946
7947
0
    return &ms->IO;
7948
0
}
7949
7950
// Return updated ImGuiMultiSelectIO structure.
7951
// Lifetime: don't hold on ImGuiMultiSelectIO* pointers over multiple frames or past any subsequent call to BeginMultiSelect() or EndMultiSelect().
7952
ImGuiMultiSelectIO* ImGui::EndMultiSelect()
7953
0
{
7954
0
    ImGuiContext& g = *GImGui;
7955
0
    ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
7956
0
    ImGuiMultiSelectState* storage = ms->Storage;
7957
0
    ImGuiWindow* window = g.CurrentWindow;
7958
0
    IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!");
7959
0
    IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow);
7960
0
    IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect);
7961
7962
0
    ImRect scope_rect = CalcScopeRect(ms, window);
7963
0
    if (ms->IsFocused)
7964
0
    {
7965
        // We currently don't allow user code to modify RangeSrcItem by writing to BeginIO's version, but that would be an easy change here.
7966
0
        if (ms->IO.RangeSrcReset || (ms->RangeSrcPassedBy == false && ms->IO.RangeSrcItem != ImGuiSelectionUserData_Invalid)) // Can't read storage->RangeSrcItem here -> we want the state at beginning of the scope (see tests for easy failure)
7967
0
        {
7968
0
            IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset RangeSrcItem.\n"); // Will set be to NavId.
7969
0
            storage->RangeSrcItem = ImGuiSelectionUserData_Invalid;
7970
0
        }
7971
0
        if (ms->NavIdPassedBy == false && storage->NavIdItem != ImGuiSelectionUserData_Invalid)
7972
0
        {
7973
0
            IMGUI_DEBUG_LOG_SELECTION("[selection] EndMultiSelect: Reset NavIdItem.\n");
7974
0
            storage->NavIdItem = ImGuiSelectionUserData_Invalid;
7975
0
            storage->NavIdSelected = -1;
7976
0
        }
7977
7978
0
        if ((ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d)) && GetBoxSelectState(ms->BoxSelectId))
7979
0
            EndBoxSelect(scope_rect, ms->Flags);
7980
0
    }
7981
7982
0
    if (ms->IsEndIO == false)
7983
0
        ms->IO.Requests.resize(0);
7984
7985
    // Clear selection when clicking void?
7986
    // We specifically test for IsMouseDragPastThreshold(0) == false to allow box-selection!
7987
    // The InnerRect test is necessary for non-child/decorated windows.
7988
0
    bool scope_hovered = IsWindowHovered() && window->InnerRect.Contains(g.IO.MousePos);
7989
0
    if (scope_hovered && (ms->Flags & ImGuiMultiSelectFlags_ScopeRect))
7990
0
        scope_hovered &= scope_rect.Contains(g.IO.MousePos);
7991
0
    if (scope_hovered && g.HoveredId == 0 && g.ActiveId == 0)
7992
0
    {
7993
0
        if (ms->Flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
7994
0
        {
7995
0
            if (!g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && g.IO.MouseClickedCount[0] == 1)
7996
0
            {
7997
0
                BoxSelectPreStartDrag(ms->BoxSelectId, ImGuiSelectionUserData_Invalid);
7998
0
                FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);
7999
0
                SetHoveredID(ms->BoxSelectId);
8000
0
                if (ms->Flags & ImGuiMultiSelectFlags_ScopeRect)
8001
0
                    SetNavID(0, ImGuiNavLayer_Main, ms->FocusScopeId, ImRect(g.IO.MousePos, g.IO.MousePos)); // Automatically switch FocusScope for initial click from void to box-select.
8002
0
            }
8003
0
        }
8004
8005
0
        if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid)
8006
0
            if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None)
8007
0
                MultiSelectAddSetAll(ms, false);
8008
0
    }
8009
8010
    // Courtesy nav wrapping helper flag
8011
0
    if (ms->Flags & ImGuiMultiSelectFlags_NavWrapX)
8012
0
    {
8013
0
        IM_ASSERT(ms->Flags & ImGuiMultiSelectFlags_ScopeWindow); // Only supported at window scope
8014
0
        ImGui::NavMoveRequestTryWrapping(ImGui::GetCurrentWindow(), ImGuiNavMoveFlags_WrapX);
8015
0
    }
8016
8017
    // Unwind
8018
0
    window->DC.CursorMaxPos = ImMax(ms->BackupCursorMaxPos, window->DC.CursorMaxPos);
8019
0
    PopFocusScope();
8020
8021
0
    if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
8022
0
        DebugLogMultiSelectRequests("EndMultiSelect", &ms->IO);
8023
8024
0
    ms->FocusScopeId = 0;
8025
0
    ms->Flags = ImGuiMultiSelectFlags_None;
8026
0
    g.CurrentMultiSelect = (--g.MultiSelectTempDataStacked > 0) ? &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] : NULL;
8027
8028
0
    return &ms->IO;
8029
0
}
8030
8031
void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)
8032
0
{
8033
    // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!
8034
    // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.
8035
0
    ImGuiContext& g = *GImGui;
8036
0
    g.NextItemData.SelectionUserData = selection_user_data;
8037
0
    g.NextItemData.FocusScopeId = g.CurrentFocusScopeId;
8038
8039
0
    if (ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect)
8040
0
    {
8041
        // Auto updating RangeSrcPassedBy for cases were clipper is not used (done before ItemAdd() clipping)
8042
0
        g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData | ImGuiItemFlags_IsMultiSelect;
8043
0
        if (ms->IO.RangeSrcItem == selection_user_data)
8044
0
            ms->RangeSrcPassedBy = true;
8045
0
    }
8046
0
    else
8047
0
    {
8048
0
        g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;
8049
0
    }
8050
0
}
8051
8052
// In charge of:
8053
// - Applying SetAll for submitted items.
8054
// - Applying SetRange for submitted items and record end points.
8055
// - Altering button behavior flags to facilitate use with drag and drop.
8056
void ImGui::MultiSelectItemHeader(ImGuiID id, bool* p_selected, ImGuiButtonFlags* p_button_flags)
8057
0
{
8058
0
    ImGuiContext& g = *GImGui;
8059
0
    ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
8060
8061
0
    bool selected = *p_selected;
8062
0
    if (ms->IsFocused)
8063
0
    {
8064
0
        ImGuiMultiSelectState* storage = ms->Storage;
8065
0
        ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData;
8066
0
        IM_ASSERT(g.NextItemData.FocusScopeId == g.CurrentFocusScopeId && "Forgot to call SetNextItemSelectionUserData() prior to item, required in BeginMultiSelect()/EndMultiSelect() scope");
8067
8068
        // Apply SetAll (Clear/SelectAll) requests requested by BeginMultiSelect().
8069
        // This is only useful if the user hasn't processed them already, and this only works if the user isn't using the clipper.
8070
        // If you are using a clipper you need to process the SetAll request after calling BeginMultiSelect()
8071
0
        if (ms->LoopRequestSetAll != -1)
8072
0
            selected = (ms->LoopRequestSetAll == 1);
8073
8074
        // When using SHIFT+Nav: because it can incur scrolling we cannot afford a frame of lag with the selection highlight (otherwise scrolling would happen before selection)
8075
        // For this to work, we need someone to set 'RangeSrcPassedBy = true' at some point (either clipper either SetNextItemSelectionUserData() function)
8076
0
        if (ms->IsKeyboardSetRange)
8077
0
        {
8078
0
            IM_ASSERT(id != 0 && (ms->KeyMods & ImGuiMod_Shift) != 0);
8079
0
            const bool is_range_dst = (ms->RangeDstPassedBy == false) && g.NavJustMovedToId == id;     // Assume that g.NavJustMovedToId is not clipped.
8080
0
            if (is_range_dst)
8081
0
                ms->RangeDstPassedBy = true;
8082
0
            if (is_range_dst && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid) // If we don't have RangeSrc, assign RangeSrc = RangeDst
8083
0
            {
8084
0
                storage->RangeSrcItem = item_data;
8085
0
                storage->RangeSelected = selected ? 1 : 0;
8086
0
            }
8087
0
            const bool is_range_src = storage->RangeSrcItem == item_data;
8088
0
            if (is_range_src || is_range_dst || ms->RangeSrcPassedBy != ms->RangeDstPassedBy)
8089
0
            {
8090
                // Apply range-select value to visible items
8091
0
                IM_ASSERT(storage->RangeSrcItem != ImGuiSelectionUserData_Invalid && storage->RangeSelected != -1);
8092
0
                selected = (storage->RangeSelected != 0);
8093
0
            }
8094
0
            else if ((ms->KeyMods & ImGuiMod_Ctrl) == 0 && (ms->Flags & ImGuiMultiSelectFlags_NoAutoClear) == 0)
8095
0
            {
8096
                // Clear other items
8097
0
                selected = false;
8098
0
            }
8099
0
        }
8100
0
        *p_selected = selected;
8101
0
    }
8102
8103
    // Alter button behavior flags
8104
    // To handle drag and drop of multiple items we need to avoid clearing selection on click.
8105
    // Enabling this test makes actions using CTRL+SHIFT delay their effect on MouseUp which is annoying, but it allows drag and drop of multiple items.
8106
0
    if (p_button_flags != NULL)
8107
0
    {
8108
0
        ImGuiButtonFlags button_flags = *p_button_flags;
8109
0
        button_flags |= ImGuiButtonFlags_NoHoveredOnFocus;
8110
0
        if ((!selected || (g.ActiveId == id && g.ActiveIdHasBeenPressedBefore)) && !(ms->Flags & ImGuiMultiSelectFlags_SelectOnClickRelease))
8111
0
            button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & ~ImGuiButtonFlags_PressedOnClickRelease;
8112
0
        else
8113
0
            button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
8114
0
        *p_button_flags = button_flags;
8115
0
    }
8116
0
}
8117
8118
// In charge of:
8119
// - Auto-select on navigation.
8120
// - Box-select toggle handling.
8121
// - Right-click handling.
8122
// - Altering selection based on Ctrl/Shift modifiers, both for keyboard and mouse.
8123
// - Record current selection state for RangeSrc
8124
// This is all rather complex, best to run and refer to "widgets_multiselect_xxx" tests in imgui_test_suite.
8125
void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
8126
0
{
8127
0
    ImGuiContext& g = *GImGui;
8128
0
    ImGuiWindow* window = g.CurrentWindow;
8129
8130
0
    bool selected = *p_selected;
8131
0
    bool pressed = *p_pressed;
8132
0
    ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect;
8133
0
    ImGuiMultiSelectState* storage = ms->Storage;
8134
0
    if (pressed)
8135
0
        ms->IsFocused = true;
8136
8137
0
    bool hovered = false;
8138
0
    if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)
8139
0
        hovered = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
8140
0
    if (!ms->IsFocused && !hovered)
8141
0
        return;
8142
8143
0
    ImGuiSelectionUserData item_data = g.NextItemData.SelectionUserData;
8144
8145
0
    ImGuiMultiSelectFlags flags = ms->Flags;
8146
0
    const bool is_singleselect = (flags & ImGuiMultiSelectFlags_SingleSelect) != 0;
8147
0
    bool is_ctrl = (ms->KeyMods & ImGuiMod_Ctrl) != 0;
8148
0
    bool is_shift = (ms->KeyMods & ImGuiMod_Shift) != 0;
8149
8150
0
    bool apply_to_range_src = false;
8151
8152
0
    if (g.NavId == id && storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)
8153
0
        apply_to_range_src = true;
8154
0
    if (ms->IsEndIO == false)
8155
0
    {
8156
0
        ms->IO.Requests.resize(0);
8157
0
        ms->IsEndIO = true;
8158
0
    }
8159
8160
    // Auto-select as you navigate a list
8161
0
    if (g.NavJustMovedToId == id)
8162
0
    {
8163
0
        if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
8164
0
        {
8165
0
            if (is_ctrl && is_shift)
8166
0
                pressed = true;
8167
0
            else if (!is_ctrl)
8168
0
                selected = pressed = true;
8169
0
        }
8170
0
        else
8171
0
        {
8172
            // With NoAutoSelect, using Shift+keyboard performs a write/copy
8173
0
            if (is_shift)
8174
0
                pressed = true;
8175
0
            else if (!is_ctrl)
8176
0
                apply_to_range_src = true; // Since if (pressed) {} main block is not running we update this
8177
0
        }
8178
0
    }
8179
8180
0
    if (apply_to_range_src)
8181
0
    {
8182
0
        storage->RangeSrcItem = item_data;
8183
0
        storage->RangeSelected = selected; // Will be updated at the end of this function anyway.
8184
0
    }
8185
8186
    // Box-select toggle handling
8187
0
    if (ms->BoxSelectId != 0)
8188
0
        if (ImGuiBoxSelectState* bs = GetBoxSelectState(ms->BoxSelectId))
8189
0
        {
8190
0
            const bool rect_overlap_curr = bs->BoxSelectRectCurr.Overlaps(g.LastItemData.Rect);
8191
0
            const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect);
8192
0
            if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr))
8193
0
            {
8194
0
                if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce)
8195
0
                {
8196
0
                    pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway)
8197
0
                    bs->IsStartedSetNavIdOnce = false;
8198
0
                }
8199
0
                else
8200
0
                {
8201
0
                    selected = !selected;
8202
0
                    MultiSelectAddSetRange(ms, selected, +1, item_data, item_data);
8203
0
                }
8204
0
                storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1);
8205
0
            }
8206
0
        }
8207
8208
    // Right-click handling.
8209
    // FIXME-MULTISELECT: Currently filtered out by ImGuiMultiSelectFlags_NoAutoSelect but maybe should be moved to Selectable(). See https://github.com/ocornut/imgui/pull/5816
8210
0
    if (hovered && IsMouseClicked(1) && (flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
8211
0
    {
8212
0
        if (g.ActiveId != 0 && g.ActiveId != id)
8213
0
            ClearActiveID();
8214
0
        SetFocusID(id, window);
8215
0
        if (!pressed && !selected)
8216
0
        {
8217
0
            pressed = true;
8218
0
            is_ctrl = is_shift = false;
8219
0
        }
8220
0
    }
8221
8222
    // Unlike Space, Enter doesn't alter selection (but can still return a press) unless current item is not selected.
8223
    // The later, "unless current item is not select", may become optional? It seems like a better default if Enter doesn't necessarily open something
8224
    // (unlike e.g. Windows explorer). For use case where Enter always open something, we might decide to make this optional?
8225
0
    const bool enter_pressed = pressed && (g.NavActivateId == id) && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput);
8226
8227
    // Alter selection
8228
0
    if (pressed && (!enter_pressed || !selected))
8229
0
    {
8230
        // Box-select
8231
0
        ImGuiInputSource input_source = (g.NavJustMovedToId == id || g.NavActivateId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;
8232
0
        if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
8233
0
            if (selected == false && !g.BoxSelectState.IsActive && !g.BoxSelectState.IsStarting && input_source == ImGuiInputSource_Mouse && g.IO.MouseClickedCount[0] == 1)
8234
0
                BoxSelectPreStartDrag(ms->BoxSelectId, item_data);
8235
8236
        //----------------------------------------------------------------------------------------
8237
        // ACTION                      | Begin  | Pressed/Activated  | End
8238
        //----------------------------------------------------------------------------------------
8239
        // Keys Navigated:             | Clear  | Src=item, Sel=1               SetRange 1
8240
        // Keys Navigated: Ctrl        | n/a    | n/a
8241
        // Keys Navigated:      Shift  | n/a    | Dst=item, Sel=1,   => Clear + SetRange 1
8242
        // Keys Navigated: Ctrl+Shift  | n/a    | Dst=item, Sel=Src  => Clear + SetRange Src-Dst
8243
        // Keys Activated:             | n/a    | Src=item, Sel=1    => Clear + SetRange 1
8244
        // Keys Activated: Ctrl        | n/a    | Src=item, Sel=!Sel =>         SetSange 1
8245
        // Keys Activated:      Shift  | n/a    | Dst=item, Sel=1    => Clear + SetSange 1
8246
        //----------------------------------------------------------------------------------------
8247
        // Mouse Pressed:              | n/a    | Src=item, Sel=1,   => Clear + SetRange 1
8248
        // Mouse Pressed:  Ctrl        | n/a    | Src=item, Sel=!Sel =>         SetRange 1
8249
        // Mouse Pressed:       Shift  | n/a    | Dst=item, Sel=1,   => Clear + SetRange 1
8250
        // Mouse Pressed:  Ctrl+Shift  | n/a    | Dst=item, Sel=!Sel =>         SetRange Src-Dst
8251
        //----------------------------------------------------------------------------------------
8252
8253
0
        if ((flags & ImGuiMultiSelectFlags_NoAutoClear) == 0)
8254
0
        {
8255
0
            bool request_clear = false;
8256
0
            if (is_singleselect)
8257
0
                request_clear = true;
8258
0
            else if ((input_source == ImGuiInputSource_Mouse || g.NavActivateId == id) && !is_ctrl)
8259
0
                request_clear = (flags & ImGuiMultiSelectFlags_NoAutoClearOnReselect) ? !selected : true;
8260
0
            else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl)
8261
0
                request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again.
8262
0
            if (request_clear)
8263
0
                MultiSelectAddSetAll(ms, false);
8264
0
        }
8265
8266
0
        int range_direction;
8267
0
        bool range_selected;
8268
0
        if (is_shift && !is_singleselect)
8269
0
        {
8270
            //IM_ASSERT(storage->HasRangeSrc && storage->HasRangeValue);
8271
0
            if (storage->RangeSrcItem == ImGuiSelectionUserData_Invalid)
8272
0
                storage->RangeSrcItem = item_data;
8273
0
            if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
8274
0
            {
8275
                // Shift+Arrow always select
8276
                // Ctrl+Shift+Arrow copy source selection state (already stored by BeginMultiSelect() in storage->RangeSelected)
8277
0
                range_selected = (is_ctrl && storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true;
8278
0
            }
8279
0
            else
8280
0
            {
8281
                // Shift+Arrow copy source selection state
8282
                // Shift+Click always copy from target selection state
8283
0
                if (ms->IsKeyboardSetRange)
8284
0
                    range_selected = (storage->RangeSelected != -1) ? (storage->RangeSelected != 0) : true;
8285
0
                else
8286
0
                    range_selected = !selected;
8287
0
            }
8288
0
            range_direction = ms->RangeSrcPassedBy ? +1 : -1;
8289
0
        }
8290
0
        else
8291
0
        {
8292
            // Ctrl inverts selection, otherwise always select
8293
0
            if ((flags & ImGuiMultiSelectFlags_NoAutoSelect) == 0)
8294
0
                selected = is_ctrl ? !selected : true;
8295
0
            else
8296
0
                selected = !selected;
8297
0
            storage->RangeSrcItem = item_data;
8298
0
            range_selected = selected;
8299
0
            range_direction = +1;
8300
0
        }
8301
0
        MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data);
8302
0
    }
8303
8304
    // Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect)
8305
0
    if (storage->RangeSrcItem == item_data)
8306
0
        storage->RangeSelected = selected ? 1 : 0;
8307
8308
    // Update/store the selection state of focused item
8309
0
    if (g.NavId == id)
8310
0
    {
8311
0
        storage->NavIdItem = item_data;
8312
0
        storage->NavIdSelected = selected ? 1 : 0;
8313
0
    }
8314
0
    if (storage->NavIdItem == item_data)
8315
0
        ms->NavIdPassedBy = true;
8316
0
    ms->LastSubmittedItem = item_data;
8317
8318
0
    *p_selected = selected;
8319
0
    *p_pressed = pressed;
8320
0
}
8321
8322
void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected)
8323
0
{
8324
0
    ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };
8325
0
    ms->IO.Requests.resize(0);      // Can always clear previous requests
8326
0
    ms->IO.Requests.push_back(req); // Add new request
8327
0
}
8328
8329
void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item)
8330
0
{
8331
    // Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges)
8332
0
    if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0)
8333
0
    {
8334
0
        ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1];
8335
0
        if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected)
8336
0
        {
8337
0
            prev->RangeLastItem = last_item;
8338
0
            return;
8339
0
        }
8340
0
    }
8341
8342
0
    ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item };
8343
0
    ms->IO.Requests.push_back(req); // Add new request
8344
0
}
8345
8346
void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
8347
0
{
8348
0
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
8349
0
    const bool is_active = (storage->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.
8350
0
    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }
8351
0
    bool open = TreeNode((void*)(intptr_t)storage->ID, "MultiSelect 0x%08X in '%s'%s", storage->ID, storage->Window ? storage->Window->Name : "N/A", is_active ? "" : " *Inactive*");
8352
0
    if (!is_active) { PopStyleColor(); }
8353
0
    if (!open)
8354
0
        return;
8355
0
    Text("RangeSrcItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), RangeSelected = %d", storage->RangeSrcItem, storage->RangeSrcItem, storage->RangeSelected);
8356
0
    Text("NavIdItem = %" IM_PRId64 " (0x%" IM_PRIX64 "), NavIdSelected = %d", storage->NavIdItem, storage->NavIdItem, storage->NavIdSelected);
8357
0
    Text("LastSelectionSize = %d", storage->LastSelectionSize); // Provided by user
8358
0
    TreePop();
8359
#else
8360
    IM_UNUSED(storage);
8361
#endif
8362
0
}
8363
8364
//-------------------------------------------------------------------------
8365
// [SECTION] Widgets: Multi-Select helpers
8366
//-------------------------------------------------------------------------
8367
// - ImGuiSelectionBasicStorage
8368
// - ImGuiSelectionExternalStorage
8369
//-------------------------------------------------------------------------
8370
8371
ImGuiSelectionBasicStorage::ImGuiSelectionBasicStorage()
8372
0
{
8373
0
    Size = 0;
8374
0
    PreserveOrder = false;
8375
0
    UserData = NULL;
8376
0
    AdapterIndexToStorageId = [](ImGuiSelectionBasicStorage*, int idx) { return (ImGuiID)idx; };
8377
0
    _SelectionOrder = 1; // Always >0
8378
0
}
8379
8380
void ImGuiSelectionBasicStorage::Clear()
8381
0
{
8382
0
    Size = 0;
8383
0
    _SelectionOrder = 1; // Always >0
8384
0
    _Storage.Data.resize(0);
8385
0
}
8386
8387
void ImGuiSelectionBasicStorage::Swap(ImGuiSelectionBasicStorage& r)
8388
0
{
8389
0
    ImSwap(Size, r.Size);
8390
0
    ImSwap(_SelectionOrder, r._SelectionOrder);
8391
0
    _Storage.Data.swap(r._Storage.Data);
8392
0
}
8393
8394
bool ImGuiSelectionBasicStorage::Contains(ImGuiID id) const
8395
0
{
8396
0
    return _Storage.GetInt(id, 0) != 0;
8397
0
}
8398
8399
static int IMGUI_CDECL PairComparerByValueInt(const void* lhs, const void* rhs)
8400
0
{
8401
0
    int lhs_v = ((const ImGuiStoragePair*)lhs)->val_i;
8402
0
    int rhs_v = ((const ImGuiStoragePair*)rhs)->val_i;
8403
0
    return (lhs_v > rhs_v ? +1 : lhs_v < rhs_v ? -1 : 0);
8404
0
}
8405
8406
// GetNextSelectedItem() is an abstraction allowing us to change our underlying actual storage system without impacting user.
8407
// (e.g. store unselected vs compact down, compact down on demand, use raw ImVector<ImGuiID> instead of ImGuiStorage...)
8408
bool ImGuiSelectionBasicStorage::GetNextSelectedItem(void** opaque_it, ImGuiID* out_id)
8409
0
{
8410
0
    ImGuiStoragePair* it = (ImGuiStoragePair*)*opaque_it;
8411
0
    ImGuiStoragePair* it_end = _Storage.Data.Data + _Storage.Data.Size;
8412
0
    if (PreserveOrder && it == NULL && it_end != NULL)
8413
0
        ImQsort(_Storage.Data.Data, (size_t)_Storage.Data.Size, sizeof(ImGuiStoragePair), PairComparerByValueInt); // ~ImGuiStorage::BuildSortByValueInt()
8414
0
    if (it == NULL)
8415
0
        it = _Storage.Data.Data;
8416
0
    IM_ASSERT(it >= _Storage.Data.Data && it <= it_end);
8417
0
    if (it != it_end)
8418
0
        while (it->val_i == 0 && it < it_end)
8419
0
            it++;
8420
0
    const bool has_more = (it != it_end);
8421
0
    *opaque_it = has_more ? (void**)(it + 1) : (void**)(it);
8422
0
    *out_id = has_more ? it->key : 0;
8423
0
    if (PreserveOrder && !has_more)
8424
0
        _Storage.BuildSortByKey();
8425
0
    return has_more;
8426
0
}
8427
8428
void ImGuiSelectionBasicStorage::SetItemSelected(ImGuiID id, bool selected)
8429
0
{
8430
0
    int* p_int = _Storage.GetIntRef(id, 0);
8431
0
    if (selected && *p_int == 0) { *p_int = _SelectionOrder++; Size++; }
8432
0
    else if (!selected && *p_int != 0) { *p_int = 0; Size--; }
8433
0
}
8434
8435
// Optimized for batch edits (with same value of 'selected')
8436
static void ImGuiSelectionBasicStorage_BatchSetItemSelected(ImGuiSelectionBasicStorage* selection, ImGuiID id, bool selected, int size_before_amends, int selection_order)
8437
0
{
8438
0
    ImGuiStorage* storage = &selection->_Storage;
8439
0
    ImGuiStoragePair* it = ImLowerBound(storage->Data.Data, storage->Data.Data + size_before_amends, id);
8440
0
    const bool is_contained = (it != storage->Data.Data + size_before_amends) && (it->key == id);
8441
0
    if (selected == (is_contained && it->val_i != 0))
8442
0
        return;
8443
0
    if (selected && !is_contained)
8444
0
        storage->Data.push_back(ImGuiStoragePair(id, selection_order)); // Push unsorted at end of vector, will be sorted in SelectionMultiAmendsFinish()
8445
0
    else if (is_contained)
8446
0
        it->val_i = selected ? selection_order : 0; // Modify in-place.
8447
0
    selection->Size += selected ? +1 : -1;
8448
0
}
8449
8450
static void ImGuiSelectionBasicStorage_BatchFinish(ImGuiSelectionBasicStorage* selection, bool selected, int size_before_amends)
8451
0
{
8452
0
    ImGuiStorage* storage = &selection->_Storage;
8453
0
    if (selected && selection->Size != size_before_amends)
8454
0
        storage->BuildSortByKey(); // When done selecting: sort everything
8455
0
}
8456
8457
// Apply requests coming from BeginMultiSelect() and EndMultiSelect().
8458
// - Enable 'Demo->Tools->Debug Log->Selection' to see selection requests as they happen.
8459
// - Honoring SetRange requests requires that you can iterate/interpolate between RangeFirstItem and RangeLastItem.
8460
//   - In this demo we often submit indices to SetNextItemSelectionUserData() + store the same indices in persistent selection.
8461
//   - Your code may do differently. If you store pointers or objects ID in ImGuiSelectionUserData you may need to perform
8462
//     a lookup in order to have some way to iterate/interpolate between two items.
8463
// - A full-featured application is likely to allow search/filtering which is likely to lead to using indices
8464
//   and constructing a view index <> object id/ptr data structure anyway.
8465
// WHEN YOUR APPLICATION SETTLES ON A CHOICE, YOU WILL PROBABLY PREFER TO GET RID OF THIS UNNECESSARY 'ImGuiSelectionBasicStorage' INDIRECTION LOGIC.
8466
// Notice that with the simplest adapter (using indices everywhere), all functions return their parameters.
8467
// The most simple implementation (using indices everywhere) would look like:
8468
//   for (ImGuiSelectionRequest& req : ms_io->Requests)
8469
//   {
8470
//      if (req.Type == ImGuiSelectionRequestType_SetAll)    { Clear(); if (req.Selected) { for (int n = 0; n < items_count; n++) { SetItemSelected(n, true); } }
8471
//      if (req.Type == ImGuiSelectionRequestType_SetRange)  { for (int n = (int)ms_io->RangeFirstItem; n <= (int)ms_io->RangeLastItem; n++) { SetItemSelected(n, ms_io->Selected); } }
8472
//   }
8473
void ImGuiSelectionBasicStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
8474
0
{
8475
    // For convenience we obtain ItemsCount as passed to BeginMultiSelect(), which is optional.
8476
    // It makes sense when using ImGuiSelectionBasicStorage to simply pass your items count to BeginMultiSelect().
8477
    // Other scheme may handle SetAll differently.
8478
0
    IM_ASSERT(ms_io->ItemsCount != -1 && "Missing value for items_count in BeginMultiSelect() call!");
8479
0
    IM_ASSERT(AdapterIndexToStorageId != NULL);
8480
8481
    // This is optimized/specialized to cope with very large selections (e.g. 100k+ items)
8482
    // - A simpler version could call SetItemSelected() directly instead of ImGuiSelectionBasicStorage_BatchSetItemSelected() + ImGuiSelectionBasicStorage_BatchFinish().
8483
    // - Optimized select can append unsorted, then sort in a second pass. Optimized unselect can clear in-place then compact in a second pass.
8484
    // - A more optimal version wouldn't even use ImGuiStorage but directly a ImVector<ImGuiID> to reduce bandwidth, but this is a reasonable trade off to reuse code.
8485
    // - There are many ways this could be better optimized. The worse case scenario being: using BoxSelect2d in a grid, box-select scrolling down while wiggling
8486
    //   left and right: it affects coarse clipping + can emit multiple SetRange with 1 item each.
8487
    // FIXME-OPT: For each block of consecutive SetRange request:
8488
    // - add all requests to a sorted list, store ID, selected, offset in ImGuiStorage.
8489
    // - rewrite sorted storage a single time.
8490
0
    for (ImGuiSelectionRequest& req : ms_io->Requests)
8491
0
    {
8492
0
        if (req.Type == ImGuiSelectionRequestType_SetAll)
8493
0
        {
8494
0
            Clear();
8495
0
            if (req.Selected)
8496
0
            {
8497
0
                _Storage.Data.reserve(ms_io->ItemsCount);
8498
0
                const int size_before_amends = _Storage.Data.Size;
8499
0
                for (int idx = 0; idx < ms_io->ItemsCount; idx++, _SelectionOrder++)
8500
0
                    ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, _SelectionOrder);
8501
0
                ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends);
8502
0
            }
8503
0
        }
8504
0
        else if (req.Type == ImGuiSelectionRequestType_SetRange)
8505
0
        {
8506
0
            const int selection_changes = (int)req.RangeLastItem - (int)req.RangeFirstItem + 1;
8507
            //ImGuiContext& g = *GImGui; IMGUI_DEBUG_LOG_SELECTION("Req %d/%d: set %d to %d\n", ms_io->Requests.index_from_ptr(&req), ms_io->Requests.Size, selection_changes, req.Selected);
8508
0
            if (selection_changes == 1 || (selection_changes < Size / 100))
8509
0
            {
8510
                // Multiple sorted insertion + copy likely to be faster.
8511
                // Technically we could do a single copy with a little more work (sort sequential SetRange requests)
8512
0
                for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)
8513
0
                    SetItemSelected(GetStorageIdFromIndex(idx), req.Selected);
8514
0
            }
8515
0
            else
8516
0
            {
8517
                // Append insertion + single sort likely be faster.
8518
                // Use req.RangeDirection to set order field so that shift+clicking from 1 to 5 is different than shift+clicking from 5 to 1
8519
0
                const int size_before_amends = _Storage.Data.Size;
8520
0
                int selection_order = _SelectionOrder + ((req.RangeDirection < 0) ? selection_changes - 1 : 0);
8521
0
                for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++, selection_order += req.RangeDirection)
8522
0
                    ImGuiSelectionBasicStorage_BatchSetItemSelected(this, GetStorageIdFromIndex(idx), req.Selected, size_before_amends, selection_order);
8523
0
                if (req.Selected)
8524
0
                    _SelectionOrder += selection_changes;
8525
0
                ImGuiSelectionBasicStorage_BatchFinish(this, req.Selected, size_before_amends);
8526
0
            }
8527
0
        }
8528
0
    }
8529
0
}
8530
8531
//-------------------------------------------------------------------------
8532
8533
ImGuiSelectionExternalStorage::ImGuiSelectionExternalStorage()
8534
0
{
8535
0
    UserData = NULL;
8536
0
    AdapterSetItemSelected = NULL;
8537
0
}
8538
8539
// Apply requests coming from BeginMultiSelect() and EndMultiSelect().
8540
// We also pull 'ms_io->ItemsCount' as passed for BeginMultiSelect() for consistency with ImGuiSelectionBasicStorage
8541
// This makes no assumption about underlying storage.
8542
void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io)
8543
0
{
8544
0
    IM_ASSERT(AdapterSetItemSelected);
8545
0
    for (ImGuiSelectionRequest& req : ms_io->Requests)
8546
0
    {
8547
0
        if (req.Type == ImGuiSelectionRequestType_SetAll)
8548
0
            for (int idx = 0; idx < ms_io->ItemsCount; idx++)
8549
0
                AdapterSetItemSelected(this, idx, req.Selected);
8550
0
        if (req.Type == ImGuiSelectionRequestType_SetRange)
8551
0
            for (int idx = (int)req.RangeFirstItem; idx <= (int)req.RangeLastItem; idx++)
8552
0
                AdapterSetItemSelected(this, idx, req.Selected);
8553
0
    }
8554
0
}
8555
8556
//-------------------------------------------------------------------------
8557
// [SECTION] Widgets: ListBox
8558
//-------------------------------------------------------------------------
8559
// - BeginListBox()
8560
// - EndListBox()
8561
// - ListBox()
8562
//-------------------------------------------------------------------------
8563
8564
// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
8565
// This handle some subtleties with capturing info from the label.
8566
// If you don't need a label you can pretty much directly use ImGui::BeginChild() with ImGuiChildFlags_FrameStyle.
8567
// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
8568
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.5f * item_height).
8569
bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
8570
0
{
8571
0
    ImGuiContext& g = *GImGui;
8572
0
    ImGuiWindow* window = GetCurrentWindow();
8573
0
    if (window->SkipItems)
8574
0
        return false;
8575
8576
0
    const ImGuiStyle& style = g.Style;
8577
0
    const ImGuiID id = GetID(label);
8578
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
8579
8580
    // Size default to hold ~7.25 items.
8581
    // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
8582
0
    ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));
8583
0
    ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
8584
0
    ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8585
0
    ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
8586
0
    g.NextItemData.ClearFlags();
8587
8588
0
    if (!IsRectVisible(bb.Min, bb.Max))
8589
0
    {
8590
0
        ItemSize(bb.GetSize(), style.FramePadding.y);
8591
0
        ItemAdd(bb, 0, &frame_bb);
8592
0
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
8593
0
        return false;
8594
0
    }
8595
8596
    // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well.
8597
0
    BeginGroup();
8598
0
    if (label_size.x > 0.0f)
8599
0
    {
8600
0
        ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y);
8601
0
        RenderText(label_pos, label);
8602
0
        window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);
8603
0
        AlignTextToFramePadding();
8604
0
    }
8605
8606
0
    BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle);
8607
0
    return true;
8608
0
}
8609
8610
void ImGui::EndListBox()
8611
0
{
8612
0
    ImGuiContext& g = *GImGui;
8613
0
    ImGuiWindow* window = g.CurrentWindow;
8614
0
    IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?");
8615
0
    IM_UNUSED(window);
8616
8617
0
    EndChild();
8618
0
    EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label
8619
0
}
8620
8621
bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
8622
0
{
8623
0
    const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
8624
0
    return value_changed;
8625
0
}
8626
8627
// This is merely a helper around BeginListBox(), EndListBox().
8628
// Considering using those directly to submit custom data or store selection differently.
8629
bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items)
8630
0
{
8631
0
    ImGuiContext& g = *GImGui;
8632
8633
    // Calculate size from "height_in_items"
8634
0
    if (height_in_items < 0)
8635
0
        height_in_items = ImMin(items_count, 7);
8636
0
    float height_in_items_f = height_in_items + 0.25f;
8637
0
    ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
8638
8639
0
    if (!BeginListBox(label, size))
8640
0
        return false;
8641
8642
    // Assume all items have even height (= 1 line of text). If you need items of different height,
8643
    // you can create a custom version of ListBox() in your code without using the clipper.
8644
0
    bool value_changed = false;
8645
0
    ImGuiListClipper clipper;
8646
0
    clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
8647
0
    clipper.IncludeItemByIndex(*current_item);
8648
0
    while (clipper.Step())
8649
0
        for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
8650
0
        {
8651
0
            const char* item_text = getter(user_data, i);
8652
0
            if (item_text == NULL)
8653
0
                item_text = "*Unknown item*";
8654
8655
0
            PushID(i);
8656
0
            const bool item_selected = (i == *current_item);
8657
0
            if (Selectable(item_text, item_selected))
8658
0
            {
8659
0
                *current_item = i;
8660
0
                value_changed = true;
8661
0
            }
8662
0
            if (item_selected)
8663
0
                SetItemDefaultFocus();
8664
0
            PopID();
8665
0
        }
8666
0
    EndListBox();
8667
8668
0
    if (value_changed)
8669
0
        MarkItemEdited(g.LastItemData.ID);
8670
8671
0
    return value_changed;
8672
0
}
8673
8674
//-------------------------------------------------------------------------
8675
// [SECTION] Widgets: PlotLines, PlotHistogram
8676
//-------------------------------------------------------------------------
8677
// - PlotEx() [Internal]
8678
// - PlotLines()
8679
// - PlotHistogram()
8680
//-------------------------------------------------------------------------
8681
// Plot/Graph widgets are not very good.
8682
// Consider writing your own, or using a third-party one, see:
8683
// - ImPlot https://github.com/epezent/implot
8684
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
8685
//-------------------------------------------------------------------------
8686
8687
int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg)
8688
0
{
8689
0
    ImGuiContext& g = *GImGui;
8690
0
    ImGuiWindow* window = GetCurrentWindow();
8691
0
    if (window->SkipItems)
8692
0
        return -1;
8693
8694
0
    const ImGuiStyle& style = g.Style;
8695
0
    const ImGuiID id = window->GetID(label);
8696
8697
0
    const ImVec2 label_size = CalcTextSize(label, NULL, true);
8698
0
    const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);
8699
8700
0
    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
8701
0
    const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);
8702
0
    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));
8703
0
    ItemSize(total_bb, style.FramePadding.y);
8704
0
    if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_NoNav))
8705
0
        return -1;
8706
0
    bool hovered;
8707
0
    ButtonBehavior(frame_bb, id, &hovered, NULL);
8708
8709
    // Determine scale from values if not specified
8710
0
    if (scale_min == FLT_MAX || scale_max == FLT_MAX)
8711
0
    {
8712
0
        float v_min = FLT_MAX;
8713
0
        float v_max = -FLT_MAX;
8714
0
        for (int i = 0; i < values_count; i++)
8715
0
        {
8716
0
            const float v = values_getter(data, i);
8717
0
            if (v != v) // Ignore NaN values
8718
0
                continue;
8719
0
            v_min = ImMin(v_min, v);
8720
0
            v_max = ImMax(v_max, v);
8721
0
        }
8722
0
        if (scale_min == FLT_MAX)
8723
0
            scale_min = v_min;
8724
0
        if (scale_max == FLT_MAX)
8725
0
            scale_max = v_max;
8726
0
    }
8727
8728
0
    RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
8729
8730
0
    const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1;
8731
0
    int idx_hovered = -1;
8732
0
    if (values_count >= values_count_min)
8733
0
    {
8734
0
        int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
8735
0
        int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);
8736
8737
        // Tooltip on hover
8738
0
        if (hovered && inner_bb.Contains(g.IO.MousePos))
8739
0
        {
8740
0
            const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);
8741
0
            const int v_idx = (int)(t * item_count);
8742
0
            IM_ASSERT(v_idx >= 0 && v_idx < values_count);
8743
8744
0
            const float v0 = values_getter(data, (v_idx + values_offset) % values_count);
8745
0
            const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);
8746
0
            if (plot_type == ImGuiPlotType_Lines)
8747
0
                SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1);
8748
0
            else if (plot_type == ImGuiPlotType_Histogram)
8749
0
                SetTooltip("%d: %8.4g", v_idx, v0);
8750
0
            idx_hovered = v_idx;
8751
0
        }
8752
8753
0
        const float t_step = 1.0f / (float)res_w;
8754
0
        const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));
8755
8756
0
        float v0 = values_getter(data, (0 + values_offset) % values_count);
8757
0
        float t0 = 0.0f;
8758
0
        ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) );                       // Point in the normalized space of our target rectangle
8759
0
        float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands
8760
8761
0
        const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);
8762
0
        const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);
8763
8764
0
        for (int n = 0; n < res_w; n++)
8765
0
        {
8766
0
            const float t1 = t0 + t_step;
8767
0
            const int v1_idx = (int)(t0 * item_count + 0.5f);
8768
0
            IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);
8769
0
            const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);
8770
0
            const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );
8771
8772
            // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.
8773
0
            ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);
8774
0
            ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));
8775
0
            if (plot_type == ImGuiPlotType_Lines)
8776
0
            {
8777
0
                window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
8778
0
            }
8779
0
            else if (plot_type == ImGuiPlotType_Histogram)
8780
0
            {
8781
0
                if (pos1.x >= pos0.x + 2.0f)
8782
0
                    pos1.x -= 1.0f;
8783
0
                window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
8784
0
            }
8785
8786
0
            t0 = t1;
8787
0
            tp0 = tp1;
8788
0
        }
8789
0
    }
8790
8791
    // Text overlay
8792
0
    if (overlay_text)
8793
0
        RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f));
8794
8795
0
    if (label_size.x > 0.0f)
8796
0
        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);
8797
8798
    // Return hovered index or -1 if none are hovered.
8799
    // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx().
8800
0
    return idx_hovered;
8801
0
}
8802
8803
struct ImGuiPlotArrayGetterData
8804
{
8805
    const float* Values;
8806
    int Stride;
8807
8808
0
    ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }
8809
};
8810
8811
static float Plot_ArrayGetter(void* data, int idx)
8812
0
{
8813
0
    ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;
8814
0
    const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);
8815
0
    return v;
8816
0
}
8817
8818
void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
8819
0
{
8820
0
    ImGuiPlotArrayGetterData data(values, stride);
8821
0
    PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8822
0
}
8823
8824
void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
8825
0
{
8826
0
    PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8827
0
}
8828
8829
void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)
8830
0
{
8831
0
    ImGuiPlotArrayGetterData data(values, stride);
8832
0
    PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8833
0
}
8834
8835
void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)
8836
0
{
8837
0
    PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
8838
0
}
8839
8840
//-------------------------------------------------------------------------
8841
// [SECTION] Widgets: Value helpers
8842
// Those is not very useful, legacy API.
8843
//-------------------------------------------------------------------------
8844
// - Value()
8845
//-------------------------------------------------------------------------
8846
8847
void ImGui::Value(const char* prefix, bool b)
8848
0
{
8849
0
    Text("%s: %s", prefix, (b ? "true" : "false"));
8850
0
}
8851
8852
void ImGui::Value(const char* prefix, int v)
8853
0
{
8854
0
    Text("%s: %d", prefix, v);
8855
0
}
8856
8857
void ImGui::Value(const char* prefix, unsigned int v)
8858
0
{
8859
0
    Text("%s: %d", prefix, v);
8860
0
}
8861
8862
void ImGui::Value(const char* prefix, float v, const char* float_format)
8863
0
{
8864
0
    if (float_format)
8865
0
    {
8866
0
        char fmt[64];
8867
0
        ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format);
8868
0
        Text(fmt, prefix, v);
8869
0
    }
8870
0
    else
8871
0
    {
8872
0
        Text("%s: %.3f", prefix, v);
8873
0
    }
8874
0
}
8875
8876
//-------------------------------------------------------------------------
8877
// [SECTION] MenuItem, BeginMenu, EndMenu, etc.
8878
//-------------------------------------------------------------------------
8879
// - ImGuiMenuColumns [Internal]
8880
// - BeginMenuBar()
8881
// - EndMenuBar()
8882
// - BeginMainMenuBar()
8883
// - EndMainMenuBar()
8884
// - BeginMenu()
8885
// - EndMenu()
8886
// - MenuItemEx() [Internal]
8887
// - MenuItem()
8888
//-------------------------------------------------------------------------
8889
8890
// Helpers for internal use
8891
void ImGuiMenuColumns::Update(float spacing, bool window_reappearing)
8892
161k
{
8893
161k
    if (window_reappearing)
8894
3
        memset(Widths, 0, sizeof(Widths));
8895
161k
    Spacing = (ImU16)spacing;
8896
161k
    CalcNextTotalWidth(true);
8897
161k
    memset(Widths, 0, sizeof(Widths));
8898
161k
    TotalWidth = NextTotalWidth;
8899
161k
    NextTotalWidth = 0;
8900
161k
}
8901
8902
void ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets)
8903
163k
{
8904
163k
    ImU16 offset = 0;
8905
163k
    bool want_spacing = false;
8906
819k
    for (int i = 0; i < IM_ARRAYSIZE(Widths); i++)
8907
655k
    {
8908
655k
        ImU16 width = Widths[i];
8909
655k
        if (want_spacing && width > 0)
8910
2.63k
            offset += Spacing;
8911
655k
        want_spacing |= (width > 0);
8912
655k
        if (update_offsets)
8913
647k
        {
8914
647k
            if (i == 1) { OffsetLabel = offset; }
8915
647k
            if (i == 2) { OffsetShortcut = offset; }
8916
647k
            if (i == 3) { OffsetMark = offset; }
8917
647k
        }
8918
655k
        offset += width;
8919
655k
    }
8920
163k
    NextTotalWidth = offset;
8921
163k
}
8922
8923
float ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark)
8924
1.98k
{
8925
1.98k
    Widths[0] = ImMax(Widths[0], (ImU16)w_icon);
8926
1.98k
    Widths[1] = ImMax(Widths[1], (ImU16)w_label);
8927
1.98k
    Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut);
8928
1.98k
    Widths[3] = ImMax(Widths[3], (ImU16)w_mark);
8929
1.98k
    CalcNextTotalWidth(false);
8930
1.98k
    return (float)ImMax(TotalWidth, NextTotalWidth);
8931
1.98k
}
8932
8933
// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere..
8934
// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer.
8935
// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary.
8936
// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars.
8937
bool ImGui::BeginMenuBar()
8938
80.5k
{
8939
80.5k
    ImGuiWindow* window = GetCurrentWindow();
8940
80.5k
    if (window->SkipItems)
8941
0
        return false;
8942
80.5k
    if (!(window->Flags & ImGuiWindowFlags_MenuBar))
8943
0
        return false;
8944
8945
80.5k
    IM_ASSERT(!window->DC.MenuBarAppending);
8946
80.5k
    BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore
8947
80.5k
    PushID("##MenuBar");
8948
8949
    // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.
8950
    // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.
8951
80.5k
    const float border_top = ImMax(window->WindowBorderSize * 0.5f - window->TitleBarHeight, 0.0f);
8952
80.5k
    ImRect bar_rect = window->MenuBarRect();
8953
80.5k
    ImRect clip_rect(IM_ROUND(bar_rect.Min.x + window->WindowBorderSize * 0.5f), IM_ROUND(bar_rect.Min.y + border_top), IM_ROUND(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize * 0.5f))), IM_ROUND(bar_rect.Max.y));
8954
80.5k
    clip_rect.ClipWith(window->OuterRectClipped);
8955
80.5k
    PushClipRect(clip_rect.Min, clip_rect.Max, false);
8956
8957
    // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags).
8958
80.5k
    window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);
8959
80.5k
    window->DC.LayoutType = ImGuiLayoutType_Horizontal;
8960
80.5k
    window->DC.IsSameLine = false;
8961
80.5k
    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;
8962
80.5k
    window->DC.MenuBarAppending = true;
8963
80.5k
    AlignTextToFramePadding();
8964
80.5k
    return true;
8965
80.5k
}
8966
8967
void ImGui::EndMenuBar()
8968
80.5k
{
8969
80.5k
    ImGuiWindow* window = GetCurrentWindow();
8970
80.5k
    if (window->SkipItems)
8971
0
        return;
8972
80.5k
    ImGuiContext& g = *GImGui;
8973
8974
80.5k
    IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
8975
80.5k
    IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);
8976
80.5k
    IM_ASSERT(window->DC.MenuBarAppending);
8977
8978
    // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.
8979
80.5k
    if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))
8980
0
    {
8981
        // Try to find out if the request is for one of our child menu
8982
0
        ImGuiWindow* nav_earliest_child = g.NavWindow;
8983
0
        while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))
8984
0
            nav_earliest_child = nav_earliest_child->ParentWindow;
8985
0
        if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)
8986
0
        {
8987
            // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.
8988
            // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering)
8989
0
            const ImGuiNavLayer layer = ImGuiNavLayer_Menu;
8990
0
            IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary)
8991
0
            FocusWindow(window);
8992
0
            SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);
8993
            // FIXME-NAV: How to deal with this when not using g.IO.ConfigNavCursorVisibleAuto?
8994
0
            if (g.NavCursorVisible)
8995
0
            {
8996
0
                g.NavCursorVisible = false; // Hide nav cursor for the current frame so we don't see the intermediary selection. Will be set again
8997
0
                g.NavCursorHideFrames = 2;
8998
0
            }
8999
0
            g.NavHighlightItemUnderNav = g.NavMousePosDirty = true;
9000
0
            NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat
9001
0
        }
9002
0
    }
9003
9004
80.5k
    PopClipRect();
9005
80.5k
    PopID();
9006
80.5k
    IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'"
9007
80.5k
    window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
9008
9009
    // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here.
9010
80.5k
    ImGuiGroupData& group_data = g.GroupStack.back();
9011
80.5k
    group_data.EmitItem = false;
9012
80.5k
    ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos;
9013
80.5k
    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent.
9014
80.5k
    EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore
9015
80.5k
    window->DC.LayoutType = ImGuiLayoutType_Vertical;
9016
80.5k
    window->DC.IsSameLine = false;
9017
80.5k
    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
9018
80.5k
    window->DC.MenuBarAppending = false;
9019
80.5k
    window->DC.CursorMaxPos = restore_cursor_max_pos;
9020
80.5k
}
9021
9022
// Important: calling order matters!
9023
// FIXME: Somehow overlapping with docking tech.
9024
// FIXME: The "rect-cut" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts)
9025
bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags)
9026
80.5k
{
9027
80.5k
    IM_ASSERT(dir != ImGuiDir_None);
9028
9029
80.5k
    ImGuiWindow* bar_window = FindWindowByName(name);
9030
80.5k
    if (bar_window == NULL || bar_window->BeginCount == 0)
9031
80.5k
    {
9032
        // Calculate and set window size/position
9033
80.5k
        ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());
9034
80.5k
        ImRect avail_rect = viewport->GetBuildWorkRect();
9035
80.5k
        ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;
9036
80.5k
        ImVec2 pos = avail_rect.Min;
9037
80.5k
        if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)
9038
0
            pos[axis] = avail_rect.Max[axis] - axis_size;
9039
80.5k
        ImVec2 size = avail_rect.GetSize();
9040
80.5k
        size[axis] = axis_size;
9041
80.5k
        SetNextWindowPos(pos);
9042
80.5k
        SetNextWindowSize(size);
9043
9044
        // Report our size into work area (for next frame) using actual window size
9045
80.5k
        if (dir == ImGuiDir_Up || dir == ImGuiDir_Left)
9046
80.5k
            viewport->BuildWorkInsetMin[axis] += axis_size;
9047
0
        else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right)
9048
0
            viewport->BuildWorkInsetMax[axis] += axis_size;
9049
80.5k
    }
9050
9051
80.5k
    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
9052
80.5k
    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
9053
80.5k
    PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint
9054
80.5k
    bool is_open = Begin(name, NULL, window_flags);
9055
80.5k
    PopStyleVar(2);
9056
9057
80.5k
    return is_open;
9058
80.5k
}
9059
9060
bool ImGui::BeginMainMenuBar()
9061
80.5k
{
9062
80.5k
    ImGuiContext& g = *GImGui;
9063
80.5k
    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();
9064
9065
    // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.
9066
    // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?
9067
    // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.
9068
80.5k
    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));
9069
80.5k
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;
9070
80.5k
    float height = GetFrameHeight();
9071
80.5k
    bool is_open = BeginViewportSideBar("##MainMenuBar", viewport, ImGuiDir_Up, height, window_flags);
9072
80.5k
    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);
9073
80.5k
    if (!is_open)
9074
0
    {
9075
0
        End();
9076
0
        return false;
9077
0
    }
9078
9079
    // Temporarily disable _NoSavedSettings, in the off-chance that tables or child windows submitted within the menu-bar may want to use settings. (#8356)
9080
80.5k
    g.CurrentWindow->Flags &= ~ImGuiWindowFlags_NoSavedSettings;
9081
80.5k
    BeginMenuBar();
9082
80.5k
    return is_open;
9083
80.5k
}
9084
9085
void ImGui::EndMainMenuBar()
9086
80.5k
{
9087
80.5k
    ImGuiContext& g = *GImGui;
9088
80.5k
    if (!g.CurrentWindow->DC.MenuBarAppending)
9089
0
    {
9090
0
        IM_ASSERT_USER_ERROR(0, "Calling EndMainMenuBar() not from a menu-bar!"); // Not technically testing that it is the main menu bar
9091
0
        return;
9092
0
    }
9093
9094
80.5k
    EndMenuBar();
9095
80.5k
    g.CurrentWindow->Flags |= ImGuiWindowFlags_NoSavedSettings; // Restore _NoSavedSettings (#8356)
9096
9097
    // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window
9098
    // FIXME: With this strategy we won't be able to restore a NULL focus.
9099
80.5k
    if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest && g.ActiveId == 0)
9100
3
        FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild);
9101
9102
80.5k
    End();
9103
80.5k
}
9104
9105
static bool IsRootOfOpenMenuSet()
9106
243k
{
9107
243k
    ImGuiContext& g = *GImGui;
9108
243k
    ImGuiWindow* window = g.CurrentWindow;
9109
243k
    if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu))
9110
241k
        return false;
9111
9112
    // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others
9113
    // (e.g. inside menu bar vs loose menu items) based on parent ID.
9114
    // This would however prevent the use of e.g. PushID() user code submitting menus.
9115
    // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,
9116
    // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.
9117
    // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup
9118
    // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu.
9119
    // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer.
9120
    // This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still
9121
    // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart
9122
    // it likely won't be a problem anyone runs into.
9123
1.97k
    const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size];
9124
1.97k
    if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer)
9125
0
        return false;
9126
1.97k
    return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true);
9127
1.97k
}
9128
9129
bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
9130
241k
{
9131
241k
    ImGuiWindow* window = GetCurrentWindow();
9132
241k
    if (window->SkipItems)
9133
0
        return false;
9134
9135
241k
    ImGuiContext& g = *GImGui;
9136
241k
    const ImGuiStyle& style = g.Style;
9137
241k
    const ImGuiID id = window->GetID(label);
9138
241k
    bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);
9139
9140
    // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)
9141
    // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.
9142
241k
    ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;
9143
241k
    if (window->Flags & ImGuiWindowFlags_ChildMenu)
9144
0
        window_flags |= ImGuiWindowFlags_ChildWindow;
9145
9146
    // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().
9147
    // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.
9148
    // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.
9149
241k
    if (g.MenusIdSubmittedThisFrame.contains(id))
9150
0
    {
9151
0
        if (menu_is_open)
9152
0
            menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
9153
0
        else
9154
0
            g.NextWindowData.ClearFlags();          // we behave like Begin() and need to consume those values
9155
0
        return menu_is_open;
9156
0
    }
9157
9158
    // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu
9159
241k
    g.MenusIdSubmittedThisFrame.push_back(id);
9160
9161
241k
    ImVec2 label_size = CalcTextSize(label, NULL, true);
9162
9163
    // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)
9164
    // This is only done for items for the menu set and not the full parent window.
9165
241k
    const bool menuset_is_open = IsRootOfOpenMenuSet();
9166
241k
    if (menuset_is_open)
9167
1.97k
        PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
9168
9169
    // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,
9170
    // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().
9171
    // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.
9172
241k
    ImVec2 popup_pos, pos = window->DC.CursorPos;
9173
241k
    PushID(label);
9174
241k
    if (!enabled)
9175
0
        BeginDisabled();
9176
241k
    const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
9177
241k
    bool pressed;
9178
9179
    // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
9180
241k
    const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_NoAutoClosePopups;
9181
241k
    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9182
241k
    {
9183
        // Menu inside a horizontal menu bar
9184
        // Selectable extend their highlight by half ItemSpacing in each direction.
9185
        // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()
9186
241k
        popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight);
9187
241k
        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
9188
241k
        PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f);
9189
241k
        float w = label_size.x;
9190
241k
        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
9191
241k
        pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y));
9192
241k
        LogSetNextTextDecoration("[", "]");
9193
241k
        RenderText(text_pos, label);
9194
241k
        PopStyleVar();
9195
241k
        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
9196
241k
    }
9197
0
    else
9198
0
    {
9199
        // Menu inside a regular/vertical menu
9200
        // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
9201
        //  Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
9202
0
        popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
9203
0
        float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
9204
0
        float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
9205
0
        float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame
9206
0
        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
9207
0
        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
9208
0
        pressed = Selectable("", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
9209
0
        LogSetNextTextDecoration("", ">");
9210
0
        RenderText(text_pos, label);
9211
0
        if (icon_w > 0.0f)
9212
0
            RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
9213
0
        RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);
9214
0
    }
9215
241k
    if (!enabled)
9216
0
        EndDisabled();
9217
9218
241k
    const bool hovered = (g.HoveredId == id) && enabled && !g.NavHighlightItemUnderNav;
9219
241k
    if (menuset_is_open)
9220
1.97k
        PopItemFlag();
9221
9222
241k
    bool want_open = false;
9223
241k
    bool want_open_nav_init = false;
9224
241k
    bool want_close = false;
9225
241k
    if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))
9226
0
    {
9227
        // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu
9228
        // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.
9229
0
        bool moving_toward_child_menu = false;
9230
0
        ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below)
9231
0
        ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL;
9232
0
        if (g.HoveredWindow == window && child_menu_window != NULL)
9233
0
        {
9234
0
            const float ref_unit = g.FontSize; // FIXME-DPI
9235
0
            const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f;
9236
0
            const ImRect next_window_rect = child_menu_window->Rect();
9237
0
            ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta);
9238
0
            ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR();
9239
0
            ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR();
9240
0
            const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack.
9241
0
            ta.x += child_dir * -0.5f;
9242
0
            tb.x += child_dir * ref_unit;
9243
0
            tc.x += child_dir * ref_unit;
9244
0
            tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus
9245
0
            tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f);
9246
0
            moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);
9247
            //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]
9248
0
        }
9249
9250
        // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
9251
        // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
9252
        // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
9253
0
        if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavHighlightItemUnderNav && g.ActiveId == 0)
9254
0
            want_close = true;
9255
9256
        // Open
9257
        // (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test)
9258
0
        if (!menu_is_open && pressed) // Click/activate to open
9259
0
            want_open = true;
9260
0
        else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open
9261
0
            want_open = true;
9262
0
        else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback)
9263
0
            want_open = true;
9264
0
        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open
9265
0
        {
9266
0
            want_open = want_open_nav_init = true;
9267
0
            NavMoveRequestCancel();
9268
0
            SetNavCursorVisibleAfterMove();
9269
0
        }
9270
0
    }
9271
241k
    else
9272
241k
    {
9273
        // Menu bar
9274
241k
        if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it
9275
0
        {
9276
0
            want_close = true;
9277
0
            want_open = menu_is_open = false;
9278
0
        }
9279
241k
        else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others
9280
1
        {
9281
1
            want_open = true;
9282
1
        }
9283
241k
        else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open
9284
0
        {
9285
0
            want_open = true;
9286
0
            NavMoveRequestCancel();
9287
0
        }
9288
241k
    }
9289
9290
241k
    if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }'
9291
0
        want_close = true;
9292
241k
    if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None))
9293
0
        ClosePopupToLevel(g.BeginPopupStack.Size, true);
9294
9295
241k
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));
9296
241k
    PopID();
9297
9298
241k
    if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)
9299
0
    {
9300
        // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame.
9301
0
        OpenPopup(label);
9302
0
    }
9303
241k
    else if (want_open)
9304
1
    {
9305
1
        menu_is_open = true;
9306
1
        OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0));
9307
1
    }
9308
9309
241k
    if (menu_is_open)
9310
660
    {
9311
660
        ImGuiLastItemData last_item_in_parent = g.LastItemData;
9312
660
        SetNextWindowPos(popup_pos, ImGuiCond_Always);                  // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.
9313
660
        PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding
9314
660
        menu_is_open = BeginPopupMenuEx(id, label, window_flags); // menu_is_open may be 'false' when the popup is completely clipped (e.g. zero size display)
9315
660
        PopStyleVar();
9316
660
        if (menu_is_open)
9317
660
        {
9318
            // Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do:
9319
            // Perform an init request in the case the popup was already open (via a previous mouse hover)
9320
660
            if (want_open && want_open_nav_init && !g.NavInitRequest)
9321
0
            {
9322
0
                FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal);
9323
0
                NavInitWindow(g.CurrentWindow, false);
9324
0
            }
9325
9326
            // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu()
9327
            // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck)
9328
660
            g.LastItemData = last_item_in_parent;
9329
660
            if (g.HoveredWindow == window)
9330
235
                g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;
9331
660
        }
9332
660
    }
9333
241k
    else
9334
241k
    {
9335
241k
        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
9336
241k
    }
9337
9338
241k
    return menu_is_open;
9339
241k
}
9340
9341
bool ImGui::BeginMenu(const char* label, bool enabled)
9342
241k
{
9343
241k
    return BeginMenuEx(label, NULL, enabled);
9344
241k
}
9345
9346
void ImGui::EndMenu()
9347
660
{
9348
    // Nav: When a left move request our menu failed, close ourselves.
9349
660
    ImGuiContext& g = *GImGui;
9350
660
    ImGuiWindow* window = g.CurrentWindow;
9351
660
    IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginMenu()/EndMenu() calls
9352
660
    ImGuiWindow* parent_window = window->ParentWindow;  // Should always be != NULL is we passed assert.
9353
660
    if (window->BeginCount == window->BeginCountPreviousFrame)
9354
659
        if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet())
9355
0
            if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical)
9356
0
            {
9357
0
                ClosePopupToLevel(g.BeginPopupStack.Size - 1, true);
9358
0
                NavMoveRequestCancel();
9359
0
            }
9360
9361
660
    EndPopup();
9362
660
}
9363
9364
bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled)
9365
1.98k
{
9366
1.98k
    ImGuiWindow* window = GetCurrentWindow();
9367
1.98k
    if (window->SkipItems)
9368
0
        return false;
9369
9370
1.98k
    ImGuiContext& g = *GImGui;
9371
1.98k
    ImGuiStyle& style = g.Style;
9372
1.98k
    ImVec2 pos = window->DC.CursorPos;
9373
1.98k
    ImVec2 label_size = CalcTextSize(label, NULL, true);
9374
9375
    // See BeginMenuEx() for comments about this.
9376
1.98k
    const bool menuset_is_open = IsRootOfOpenMenuSet();
9377
1.98k
    if (menuset_is_open)
9378
0
        PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);
9379
9380
    // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),
9381
    // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.
9382
1.98k
    bool pressed;
9383
1.98k
    PushID(label);
9384
1.98k
    if (!enabled)
9385
0
        BeginDisabled();
9386
9387
    // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.
9388
1.98k
    const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover;
9389
1.98k
    const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;
9390
1.98k
    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
9391
0
    {
9392
        // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
9393
        // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
9394
0
        float w = label_size.x;
9395
0
        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
9396
0
        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
9397
0
        PushStyleVarX(ImGuiStyleVar_ItemSpacing, style.ItemSpacing.x * 2.0f);
9398
0
        pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f));
9399
0
        PopStyleVar();
9400
0
        if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
9401
0
            RenderText(text_pos, label);
9402
0
        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
9403
0
    }
9404
1.98k
    else
9405
1.98k
    {
9406
        // Menu item inside a vertical menu
9407
        // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
9408
        //  Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.)
9409
1.98k
        float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
9410
1.98k
        float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;
9411
1.98k
        float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
9412
1.98k
        float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
9413
1.98k
        float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
9414
1.98k
        pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
9415
1.98k
        if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
9416
1.97k
        {
9417
1.97k
            RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);
9418
1.97k
            if (icon_w > 0.0f)
9419
0
                RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);
9420
1.97k
            if (shortcut_w > 0.0f)
9421
0
            {
9422
0
                PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
9423
0
                LogSetNextTextDecoration("(", ")");
9424
0
                RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false);
9425
0
                PopStyleColor();
9426
0
            }
9427
1.97k
            if (selected)
9428
659
                RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);
9429
1.97k
        }
9430
1.98k
    }
9431
1.98k
    IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));
9432
1.98k
    if (!enabled)
9433
0
        EndDisabled();
9434
1.98k
    PopID();
9435
1.98k
    if (menuset_is_open)
9436
0
        PopItemFlag();
9437
9438
1.98k
    return pressed;
9439
1.98k
}
9440
9441
bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
9442
1.98k
{
9443
1.98k
    return MenuItemEx(label, NULL, shortcut, selected, enabled);
9444
1.98k
}
9445
9446
bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)
9447
0
{
9448
0
    if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled))
9449
0
    {
9450
0
        if (p_selected)
9451
0
            *p_selected = !*p_selected;
9452
0
        return true;
9453
0
    }
9454
0
    return false;
9455
0
}
9456
9457
//-------------------------------------------------------------------------
9458
// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.
9459
//-------------------------------------------------------------------------
9460
// - BeginTabBar()
9461
// - BeginTabBarEx() [Internal]
9462
// - EndTabBar()
9463
// - TabBarLayout() [Internal]
9464
// - TabBarCalcTabID() [Internal]
9465
// - TabBarCalcMaxTabWidth() [Internal]
9466
// - TabBarFindTabById() [Internal]
9467
// - TabBarFindTabByOrder() [Internal]
9468
// - TabBarGetCurrentTab() [Internal]
9469
// - TabBarGetTabName() [Internal]
9470
// - TabBarRemoveTab() [Internal]
9471
// - TabBarCloseTab() [Internal]
9472
// - TabBarScrollClamp() [Internal]
9473
// - TabBarScrollToTab() [Internal]
9474
// - TabBarQueueFocus() [Internal]
9475
// - TabBarQueueReorder() [Internal]
9476
// - TabBarProcessReorderFromMousePos() [Internal]
9477
// - TabBarProcessReorder() [Internal]
9478
// - TabBarScrollingButtons() [Internal]
9479
// - TabBarTabListPopupButton() [Internal]
9480
//-------------------------------------------------------------------------
9481
9482
struct ImGuiTabBarSection
9483
{
9484
    int                 TabCount;               // Number of tabs in this section.
9485
    float               Width;                  // Sum of width of tabs in this section (after shrinking down)
9486
    float               WidthAfterShrinkMinWidth;
9487
    float               Spacing;                // Horizontal spacing at the end of the section.
9488
9489
0
    ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); }
9490
};
9491
9492
namespace ImGui
9493
{
9494
    static void             TabBarLayout(ImGuiTabBar* tab_bar);
9495
    static ImU32            TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window);
9496
    static float            TabBarCalcMaxTabWidth();
9497
    static float            TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);
9498
    static void             TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections);
9499
    static ImGuiTabItem*    TabBarScrollingButtons(ImGuiTabBar* tab_bar);
9500
    static ImGuiTabItem*    TabBarTabListPopupButton(ImGuiTabBar* tab_bar);
9501
}
9502
9503
ImGuiTabBar::ImGuiTabBar()
9504
0
{
9505
0
    memset(this, 0, sizeof(*this));
9506
0
    CurrFrameVisible = PrevFrameVisible = -1;
9507
0
    LastTabItemIdx = -1;
9508
0
}
9509
9510
static inline int TabItemGetSectionIdx(const ImGuiTabItem* tab)
9511
0
{
9512
0
    return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;
9513
0
}
9514
9515
static int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs)
9516
0
{
9517
0
    const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
9518
0
    const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
9519
0
    const int a_section = TabItemGetSectionIdx(a);
9520
0
    const int b_section = TabItemGetSectionIdx(b);
9521
0
    if (a_section != b_section)
9522
0
        return a_section - b_section;
9523
0
    return (int)(a->IndexDuringLayout - b->IndexDuringLayout);
9524
0
}
9525
9526
static int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs)
9527
0
{
9528
0
    const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;
9529
0
    const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;
9530
0
    return (int)(a->BeginOrder - b->BeginOrder);
9531
0
}
9532
9533
static ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref)
9534
0
{
9535
0
    ImGuiContext& g = *GImGui;
9536
0
    return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index);
9537
0
}
9538
9539
static ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar)
9540
0
{
9541
0
    ImGuiContext& g = *GImGui;
9542
0
    if (g.TabBars.Contains(tab_bar))
9543
0
        return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar));
9544
0
    return ImGuiPtrOrIndex(tab_bar);
9545
0
}
9546
9547
ImGuiTabBar* ImGui::TabBarFindByID(ImGuiID id)
9548
0
{
9549
0
    ImGuiContext& g = *GImGui;
9550
0
    return g.TabBars.GetByKey(id);
9551
0
}
9552
9553
// Remove TabBar data (currently only used by TestEngine)
9554
void    ImGui::TabBarRemove(ImGuiTabBar* tab_bar)
9555
0
{
9556
0
    ImGuiContext& g = *GImGui;
9557
0
    g.TabBars.Remove(tab_bar->ID, tab_bar);
9558
0
}
9559
9560
bool    ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
9561
0
{
9562
0
    ImGuiContext& g = *GImGui;
9563
0
    ImGuiWindow* window = g.CurrentWindow;
9564
0
    if (window->SkipItems)
9565
0
        return false;
9566
9567
0
    ImGuiID id = window->GetID(str_id);
9568
0
    ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);
9569
0
    ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);
9570
0
    tab_bar->ID = id;
9571
0
    tab_bar->SeparatorMinX = tab_bar_bb.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f);
9572
0
    tab_bar->SeparatorMaxX = tab_bar_bb.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f);
9573
    //if (g.NavWindow && IsWindowChildOf(g.NavWindow, window, false, false))
9574
0
    flags |= ImGuiTabBarFlags_IsFocused;
9575
0
    return BeginTabBarEx(tab_bar, tab_bar_bb, flags);
9576
0
}
9577
9578
bool    ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)
9579
0
{
9580
0
    ImGuiContext& g = *GImGui;
9581
0
    ImGuiWindow* window = g.CurrentWindow;
9582
0
    if (window->SkipItems)
9583
0
        return false;
9584
9585
0
    IM_ASSERT(tab_bar->ID != 0);
9586
0
    if ((flags & ImGuiTabBarFlags_DockNode) == 0)
9587
0
        PushOverrideID(tab_bar->ID);
9588
9589
    // Add to stack
9590
0
    g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar));
9591
0
    g.CurrentTabBar = tab_bar;
9592
0
    tab_bar->Window = window;
9593
9594
    // Append with multiple BeginTabBar()/EndTabBar() pairs.
9595
0
    tab_bar->BackupCursorPos = window->DC.CursorPos;
9596
0
    if (tab_bar->CurrFrameVisible == g.FrameCount)
9597
0
    {
9598
0
        window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);
9599
0
        tab_bar->BeginCount++;
9600
0
        return true;
9601
0
    }
9602
9603
    // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable
9604
0
    if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable)))
9605
0
        ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);
9606
0
    tab_bar->TabsAddedNew = false;
9607
9608
    // Flags
9609
0
    if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)
9610
0
        flags |= ImGuiTabBarFlags_FittingPolicyDefault_;
9611
9612
0
    tab_bar->Flags = flags;
9613
0
    tab_bar->BarRect = tab_bar_bb;
9614
0
    tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab()
9615
0
    tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;
9616
0
    tab_bar->CurrFrameVisible = g.FrameCount;
9617
0
    tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight;
9618
0
    tab_bar->CurrTabsContentsHeight = 0.0f;
9619
0
    tab_bar->ItemSpacingY = g.Style.ItemSpacing.y;
9620
0
    tab_bar->FramePadding = g.Style.FramePadding;
9621
0
    tab_bar->TabsActiveCount = 0;
9622
0
    tab_bar->LastTabItemIdx = -1;
9623
0
    tab_bar->BeginCount = 1;
9624
9625
    // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap
9626
0
    window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);
9627
9628
    // Draw separator
9629
    // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable)
9630
0
    const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected);
9631
0
    if (g.Style.TabBarBorderSize > 0.0f)
9632
0
    {
9633
0
        const float y = tab_bar->BarRect.Max.y;
9634
0
        window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col);
9635
0
    }
9636
0
    return true;
9637
0
}
9638
9639
void    ImGui::EndTabBar()
9640
0
{
9641
0
    ImGuiContext& g = *GImGui;
9642
0
    ImGuiWindow* window = g.CurrentWindow;
9643
0
    if (window->SkipItems)
9644
0
        return;
9645
9646
0
    ImGuiTabBar* tab_bar = g.CurrentTabBar;
9647
0
    if (tab_bar == NULL)
9648
0
    {
9649
0
        IM_ASSERT_USER_ERROR(tab_bar != NULL, "Mismatched BeginTabBar()/EndTabBar()!");
9650
0
        return;
9651
0
    }
9652
9653
    // Fallback in case no TabItem have been submitted
9654
0
    if (tab_bar->WantLayout)
9655
0
        TabBarLayout(tab_bar);
9656
9657
    // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().
9658
0
    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
9659
0
    if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)
9660
0
    {
9661
0
        tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight);
9662
0
        window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight;
9663
0
    }
9664
0
    else
9665
0
    {
9666
0
        window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight;
9667
0
    }
9668
0
    if (tab_bar->BeginCount > 1)
9669
0
        window->DC.CursorPos = tab_bar->BackupCursorPos;
9670
9671
0
    tab_bar->LastTabItemIdx = -1;
9672
0
    if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)
9673
0
        PopID();
9674
9675
0
    g.CurrentTabBarStack.pop_back();
9676
0
    g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back());
9677
0
}
9678
9679
// Scrolling happens only in the central section (leading/trailing sections are not scrolling)
9680
static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections)
9681
0
{
9682
0
    return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing;
9683
0
}
9684
9685
// This is called only once a frame before by the first call to ItemTab()
9686
// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions.
9687
static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
9688
0
{
9689
0
    ImGuiContext& g = *GImGui;
9690
0
    tab_bar->WantLayout = false;
9691
9692
    // Track selected tab when resizing our parent down
9693
0
    const bool scroll_to_selected_tab = (tab_bar->BarRectPrevWidth > tab_bar->BarRect.GetWidth());
9694
0
    tab_bar->BarRectPrevWidth = tab_bar->BarRect.GetWidth();
9695
9696
    // Garbage collect by compacting list
9697
    // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section)
9698
0
    int tab_dst_n = 0;
9699
0
    bool need_sort_by_section = false;
9700
0
    ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing
9701
0
    for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)
9702
0
    {
9703
0
        ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];
9704
0
        if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose)
9705
0
        {
9706
            // Remove tab
9707
0
            if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; }
9708
0
            if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; }
9709
0
            if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; }
9710
0
            continue;
9711
0
        }
9712
0
        if (tab_dst_n != tab_src_n)
9713
0
            tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];
9714
9715
0
        tab = &tab_bar->Tabs[tab_dst_n];
9716
0
        tab->IndexDuringLayout = (ImS16)tab_dst_n;
9717
9718
        // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another)
9719
0
        int curr_tab_section_n = TabItemGetSectionIdx(tab);
9720
0
        if (tab_dst_n > 0)
9721
0
        {
9722
0
            ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1];
9723
0
            int prev_tab_section_n = TabItemGetSectionIdx(prev_tab);
9724
0
            if (curr_tab_section_n == 0 && prev_tab_section_n != 0)
9725
0
                need_sort_by_section = true;
9726
0
            if (prev_tab_section_n == 2 && curr_tab_section_n != 2)
9727
0
                need_sort_by_section = true;
9728
0
        }
9729
9730
0
        sections[curr_tab_section_n].TabCount++;
9731
0
        tab_dst_n++;
9732
0
    }
9733
0
    if (tab_bar->Tabs.Size != tab_dst_n)
9734
0
        tab_bar->Tabs.resize(tab_dst_n);
9735
9736
0
    if (need_sort_by_section)
9737
0
        ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection);
9738
9739
    // Calculate spacing between sections
9740
0
    sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
9741
0
    sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;
9742
9743
    // Setup next selected tab
9744
0
    ImGuiID scroll_to_tab_id = 0;
9745
0
    if (tab_bar->NextSelectedTabId)
9746
0
    {
9747
0
        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;
9748
0
        tab_bar->NextSelectedTabId = 0;
9749
0
        scroll_to_tab_id = tab_bar->SelectedTabId;
9750
0
    }
9751
9752
    // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot).
9753
0
    if (tab_bar->ReorderRequestTabId != 0)
9754
0
    {
9755
0
        if (TabBarProcessReorder(tab_bar))
9756
0
            if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId)
9757
0
                scroll_to_tab_id = tab_bar->ReorderRequestTabId;
9758
0
        tab_bar->ReorderRequestTabId = 0;
9759
0
    }
9760
9761
    // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!)
9762
0
    const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;
9763
0
    if (tab_list_popup_button)
9764
0
        if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x!
9765
0
            scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;
9766
9767
    // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central
9768
    // (whereas our tabs are stored as: leading, central, trailing)
9769
0
    int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount };
9770
0
    g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size);
9771
9772
    // Minimum shrink width
9773
0
    const float shrink_min_width = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed) ? g.Style.TabMinWidthShrink : 1.0f;
9774
9775
    // Compute ideal tabs widths + store them into shrink buffer
9776
0
    ImGuiTabItem* most_recently_selected_tab = NULL;
9777
0
    int curr_section_n = -1;
9778
0
    bool found_selected_tab_id = false;
9779
0
    for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
9780
0
    {
9781
0
        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
9782
0
        IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);
9783
9784
0
        if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button))
9785
0
            most_recently_selected_tab = tab;
9786
0
        if (tab->ID == tab_bar->SelectedTabId)
9787
0
            found_selected_tab_id = true;
9788
0
        if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID)
9789
0
            scroll_to_tab_id = tab->ID;
9790
9791
        // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.
9792
        // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,
9793
        // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.
9794
0
        const char* tab_name = TabBarGetTabName(tab_bar, tab);
9795
0
        const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument);
9796
0
        tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x;
9797
0
        if ((tab->Flags & ImGuiTabItemFlags_Button) == 0)
9798
0
            tab->ContentWidth = ImMax(tab->ContentWidth, g.Style.TabMinWidthBase);
9799
9800
0
        int section_n = TabItemGetSectionIdx(tab);
9801
0
        ImGuiTabBarSection* section = &sections[section_n];
9802
0
        section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f);
9803
0
        section->WidthAfterShrinkMinWidth += ImMin(tab->ContentWidth, shrink_min_width) + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f);
9804
0
        curr_section_n = section_n;
9805
9806
        // Store data so we can build an array sorted by width if we need to shrink tabs down
9807
0
        IM_MSVC_WARNING_SUPPRESS(6385);
9808
0
        ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++];
9809
0
        shrink_width_item->Index = tab_n;
9810
0
        shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth;
9811
0
        tab->Width = ImMax(tab->ContentWidth, 1.0f);
9812
0
    }
9813
9814
    // Compute total ideal width (used for e.g. auto-resizing a window)
9815
0
    float width_all_tabs_after_min_width_shrink = 0.0f;
9816
0
    tab_bar->WidthAllTabsIdeal = 0.0f;
9817
0
    for (int section_n = 0; section_n < 3; section_n++)
9818
0
    {
9819
0
        tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing;
9820
0
        width_all_tabs_after_min_width_shrink += sections[section_n].WidthAfterShrinkMinWidth + sections[section_n].Spacing;
9821
0
    }
9822
9823
    // Horizontal scrolling buttons
9824
    // Important: note that TabBarScrollButtons() will alter BarRect.Max.x.
9825
0
    const bool can_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed);
9826
0
    const float width_all_tabs_to_use_for_scroll = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) ? tab_bar->WidthAllTabs : width_all_tabs_after_min_width_shrink;
9827
0
    tab_bar->ScrollButtonEnabled = ((width_all_tabs_to_use_for_scroll > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && can_scroll);
9828
0
    if (tab_bar->ScrollButtonEnabled)
9829
0
        if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar))
9830
0
        {
9831
0
            scroll_to_tab_id = scroll_and_select_tab->ID;
9832
0
            if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0)
9833
0
                tab_bar->SelectedTabId = scroll_to_tab_id;
9834
0
        }
9835
0
    if (scroll_to_tab_id == 0 && scroll_to_selected_tab)
9836
0
        scroll_to_tab_id = tab_bar->SelectedTabId;
9837
9838
    // Shrink widths if full tabs don't fit in their allocated space
9839
0
    float section_0_w = sections[0].Width + sections[0].Spacing;
9840
0
    float section_1_w = sections[1].Width + sections[1].Spacing;
9841
0
    float section_2_w = sections[2].Width + sections[2].Spacing;
9842
0
    bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth();
9843
0
    float width_excess;
9844
0
    if (central_section_is_visible)
9845
0
        width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section
9846
0
    else
9847
0
        width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section
9848
9849
    // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore
9850
0
    const bool can_shrink = (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyShrink) || (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyMixed);
9851
0
    if (width_excess >= 1.0f && (can_shrink || !central_section_is_visible))
9852
0
    {
9853
0
        int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount);
9854
0
        int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0);
9855
0
        ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess, shrink_min_width);
9856
9857
        // Apply shrunk values into tabs and sections
9858
0
        for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)
9859
0
        {
9860
0
            ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];
9861
0
            float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width);
9862
0
            if (shrinked_width < 0.0f)
9863
0
                continue;
9864
9865
0
            shrinked_width = ImMax(1.0f, shrinked_width);
9866
0
            int section_n = TabItemGetSectionIdx(tab);
9867
0
            sections[section_n].Width -= (tab->Width - shrinked_width);
9868
0
            tab->Width = shrinked_width;
9869
0
        }
9870
0
    }
9871
9872
    // Layout all active tabs
9873
0
    int section_tab_index = 0;
9874
0
    float tab_offset = 0.0f;
9875
0
    tab_bar->WidthAllTabs = 0.0f;
9876
0
    for (int section_n = 0; section_n < 3; section_n++)
9877
0
    {
9878
0
        ImGuiTabBarSection* section = &sections[section_n];
9879
0
        if (section_n == 2)
9880
0
            tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset);
9881
9882
0
        for (int tab_n = 0; tab_n < section->TabCount; tab_n++)
9883
0
        {
9884
0
            ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];
9885
0
            tab->Offset = tab_offset;
9886
0
            tab->NameOffset = -1;
9887
0
            tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);
9888
0
        }
9889
0
        tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f);
9890
0
        tab_offset += section->Spacing;
9891
0
        section_tab_index += section->TabCount;
9892
0
    }
9893
9894
    // Clear name buffers
9895
0
    tab_bar->TabsNames.Buf.resize(0);
9896
9897
    // If we have lost the selected tab, select the next most recently active one
9898
0
    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
9899
0
    if (found_selected_tab_id == false && !tab_bar_appearing)
9900
0
        tab_bar->SelectedTabId = 0;
9901
0
    if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)
9902
0
        scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;
9903
9904
    // Lock in visible tab
9905
0
    tab_bar->VisibleTabId = tab_bar->SelectedTabId;
9906
0
    tab_bar->VisibleTabWasSubmitted = false;
9907
9908
    // Apply request requests
9909
0
    if (scroll_to_tab_id != 0)
9910
0
        TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections);
9911
0
    else if (tab_bar->ScrollButtonEnabled && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow))
9912
0
    {
9913
0
        const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH;
9914
0
        const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX;
9915
0
        if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f)
9916
0
        {
9917
0
            const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f;
9918
0
            tab_bar->ScrollingTargetDistToVisibility = 0.0f;
9919
0
            tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step);
9920
0
        }
9921
0
        SetKeyOwner(wheel_key, tab_bar->ID);
9922
0
    }
9923
9924
    // Update scrolling
9925
0
    tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);
9926
0
    tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);
9927
0
    if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)
9928
0
    {
9929
        // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds.
9930
        // Teleport if we are aiming far off the visible line
9931
0
        tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize);
9932
0
        tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f);
9933
0
        const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize);
9934
0
        tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed);
9935
0
    }
9936
0
    else
9937
0
    {
9938
0
        tab_bar->ScrollingSpeed = 0.0f;
9939
0
    }
9940
0
    tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing;
9941
0
    tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing;
9942
9943
    // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame)
9944
0
    ImGuiWindow* window = g.CurrentWindow;
9945
0
    window->DC.CursorPos = tab_bar->BarRect.Min;
9946
0
    ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);
9947
0
    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal);
9948
0
}
9949
9950
// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack.
9951
static ImU32   ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window)
9952
0
{
9953
0
    IM_ASSERT(docked_window == NULL); // master branch only
9954
0
    IM_UNUSED(docked_window);
9955
0
    if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
9956
0
    {
9957
0
        ImGuiID id = ImHashStr(label);
9958
0
        KeepAliveID(id);
9959
0
        return id;
9960
0
    }
9961
0
    else
9962
0
    {
9963
0
        ImGuiWindow* window = GImGui->CurrentWindow;
9964
0
        return window->GetID(label);
9965
0
    }
9966
0
}
9967
9968
static float ImGui::TabBarCalcMaxTabWidth()
9969
0
{
9970
0
    ImGuiContext& g = *GImGui;
9971
0
    return g.FontSize * 20.0f;
9972
0
}
9973
9974
ImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id)
9975
0
{
9976
0
    if (tab_id != 0)
9977
0
        for (int n = 0; n < tab_bar->Tabs.Size; n++)
9978
0
            if (tab_bar->Tabs[n].ID == tab_id)
9979
0
                return &tab_bar->Tabs[n];
9980
0
    return NULL;
9981
0
}
9982
9983
// Order = visible order, not submission order! (which is tab->BeginOrder)
9984
ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order)
9985
0
{
9986
0
    if (order < 0 || order >= tab_bar->Tabs.Size)
9987
0
        return NULL;
9988
0
    return &tab_bar->Tabs[order];
9989
0
}
9990
9991
ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)
9992
0
{
9993
0
    if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size)
9994
0
        return NULL;
9995
0
    return &tab_bar->Tabs[tab_bar->LastTabItemIdx];
9996
0
}
9997
9998
const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
9999
0
{
10000
0
    if (tab->NameOffset == -1)
10001
0
        return "N/A";
10002
0
    IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size);
10003
0
    return tab_bar->TabsNames.Buf.Data + tab->NameOffset;
10004
0
}
10005
10006
// The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.
10007
void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)
10008
0
{
10009
0
    if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
10010
0
        tab_bar->Tabs.erase(tab);
10011
0
    if (tab_bar->VisibleTabId == tab_id)      { tab_bar->VisibleTabId = 0; }
10012
0
    if (tab_bar->SelectedTabId == tab_id)     { tab_bar->SelectedTabId = 0; }
10013
0
    if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }
10014
0
}
10015
10016
// Called on manual closure attempt
10017
void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
10018
0
{
10019
0
    if (tab->Flags & ImGuiTabItemFlags_Button)
10020
0
        return; // A button appended with TabItemButton().
10021
10022
0
    if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0)
10023
0
    {
10024
        // This will remove a frame of lag for selecting another tab on closure.
10025
        // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure
10026
0
        tab->WantClose = true;
10027
0
        if (tab_bar->VisibleTabId == tab->ID)
10028
0
        {
10029
0
            tab->LastFrameVisible = -1;
10030
0
            tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;
10031
0
        }
10032
0
    }
10033
0
    else
10034
0
    {
10035
        // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup)
10036
0
        if (tab_bar->VisibleTabId != tab->ID)
10037
0
            TabBarQueueFocus(tab_bar, tab);
10038
0
    }
10039
0
}
10040
10041
static float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)
10042
0
{
10043
0
    scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth());
10044
0
    return ImMax(scrolling, 0.0f);
10045
0
}
10046
10047
// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys
10048
static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections)
10049
0
{
10050
0
    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id);
10051
0
    if (tab == NULL)
10052
0
        return;
10053
0
    if (tab->Flags & ImGuiTabItemFlags_SectionMask_)
10054
0
        return;
10055
10056
0
    ImGuiContext& g = *GImGui;
10057
0
    float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)
10058
0
    int order = TabBarGetTabOrder(tab_bar, tab);
10059
10060
    // Scrolling happens only in the central section (leading/trailing sections are not scrolling)
10061
0
    float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections);
10062
10063
    // We make all tabs positions all relative Sections[0].Width to make code simpler
10064
0
    float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f);
10065
0
    float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f);
10066
0
    tab_bar->ScrollingTargetDistToVisibility = 0.0f;
10067
0
    if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width))
10068
0
    {
10069
        // Scroll to the left
10070
0
        tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f);
10071
0
        tab_bar->ScrollingTarget = tab_x1;
10072
0
    }
10073
0
    else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width)
10074
0
    {
10075
        // Scroll to the right
10076
0
        tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f);
10077
0
        tab_bar->ScrollingTarget = tab_x2 - scrollable_width;
10078
0
    }
10079
0
}
10080
10081
void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)
10082
0
{
10083
0
    tab_bar->NextSelectedTabId = tab->ID;
10084
0
}
10085
10086
void ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, const char* tab_name)
10087
0
{
10088
0
    IM_ASSERT((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0); // Only supported for manual/explicit tab bars
10089
0
    ImGuiID tab_id = TabBarCalcTabID(tab_bar, tab_name, NULL);
10090
0
    tab_bar->NextSelectedTabId = tab_id;
10091
0
}
10092
10093
void ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset)
10094
0
{
10095
0
    IM_ASSERT(offset != 0);
10096
0
    IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
10097
0
    tab_bar->ReorderRequestTabId = tab->ID;
10098
0
    tab_bar->ReorderRequestOffset = (ImS16)offset;
10099
0
}
10100
10101
void ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos)
10102
0
{
10103
0
    ImGuiContext& g = *GImGui;
10104
0
    IM_ASSERT(tab_bar->ReorderRequestTabId == 0);
10105
0
    if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0)
10106
0
        return;
10107
10108
0
    const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
10109
0
    const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0);
10110
10111
    // Count number of contiguous tabs we are crossing over
10112
0
    const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1;
10113
0
    const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab);
10114
0
    int dst_idx = src_idx;
10115
0
    for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir)
10116
0
    {
10117
        // Reordered tabs must share the same section
10118
0
        const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i];
10119
0
        if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder)
10120
0
            break;
10121
0
        if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_))
10122
0
            break;
10123
0
        dst_idx = i;
10124
10125
        // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered.
10126
0
        const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x;
10127
0
        const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x;
10128
        //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255));
10129
0
        if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2))
10130
0
            break;
10131
0
    }
10132
10133
0
    if (dst_idx != src_idx)
10134
0
        TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx);
10135
0
}
10136
10137
bool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)
10138
0
{
10139
0
    ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId);
10140
0
    if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder))
10141
0
        return false;
10142
10143
    //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools
10144
0
    int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset;
10145
0
    if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)
10146
0
        return false;
10147
10148
    // Reordered tabs must share the same section
10149
    // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too)
10150
0
    ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];
10151
0
    if (tab2->Flags & ImGuiTabItemFlags_NoReorder)
10152
0
        return false;
10153
0
    if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_))
10154
0
        return false;
10155
10156
0
    ImGuiTabItem item_tmp = *tab1;
10157
0
    ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2;
10158
0
    ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1;
10159
0
    const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset;
10160
0
    memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem));
10161
0
    *tab2 = item_tmp;
10162
10163
0
    if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)
10164
0
        MarkIniSettingsDirty();
10165
0
    return true;
10166
0
}
10167
10168
static ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)
10169
0
{
10170
0
    ImGuiContext& g = *GImGui;
10171
0
    ImGuiWindow* window = g.CurrentWindow;
10172
10173
0
    const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);
10174
0
    const float scrolling_buttons_width = arrow_button_size.x * 2.0f;
10175
10176
0
    const ImVec2 backup_cursor_pos = window->DC.CursorPos;
10177
    //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255));
10178
10179
0
    int select_dir = 0;
10180
0
    ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
10181
0
    arrow_col.w *= 0.5f;
10182
10183
0
    PushStyleColor(ImGuiCol_Text, arrow_col);
10184
0
    PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
10185
0
    PushItemFlag(ImGuiItemFlags_ButtonRepeat | ImGuiItemFlags_NoNav, true);
10186
0
    const float backup_repeat_delay = g.IO.KeyRepeatDelay;
10187
0
    const float backup_repeat_rate = g.IO.KeyRepeatRate;
10188
0
    g.IO.KeyRepeatDelay = 0.250f;
10189
0
    g.IO.KeyRepeatRate = 0.200f;
10190
0
    float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width);
10191
0
    window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y);
10192
0
    if (ArrowButtonEx("##<", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick))
10193
0
        select_dir = -1;
10194
0
    window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y);
10195
0
    if (ArrowButtonEx("##>", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick))
10196
0
        select_dir = +1;
10197
0
    PopItemFlag();
10198
0
    PopStyleColor(2);
10199
0
    g.IO.KeyRepeatRate = backup_repeat_rate;
10200
0
    g.IO.KeyRepeatDelay = backup_repeat_delay;
10201
10202
0
    ImGuiTabItem* tab_to_scroll_to = NULL;
10203
0
    if (select_dir != 0)
10204
0
        if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))
10205
0
        {
10206
0
            int selected_order = TabBarGetTabOrder(tab_bar, tab_item);
10207
0
            int target_order = selected_order + select_dir;
10208
10209
            // Skip tab item buttons until another tab item is found or end is reached
10210
0
            while (tab_to_scroll_to == NULL)
10211
0
            {
10212
                // If we are at the end of the list, still scroll to make our tab visible
10213
0
                tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order];
10214
10215
                // Cross through buttons
10216
                // (even if first/last item is a button, return it so we can update the scroll)
10217
0
                if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button)
10218
0
                {
10219
0
                    target_order += select_dir;
10220
0
                    selected_order += select_dir;
10221
0
                    tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL;
10222
0
                }
10223
0
            }
10224
0
        }
10225
0
    window->DC.CursorPos = backup_cursor_pos;
10226
0
    tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;
10227
10228
0
    return tab_to_scroll_to;
10229
0
}
10230
10231
static ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)
10232
0
{
10233
0
    ImGuiContext& g = *GImGui;
10234
0
    ImGuiWindow* window = g.CurrentWindow;
10235
10236
    // We use g.Style.FramePadding.y to match the square ArrowButton size
10237
0
    const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y;
10238
0
    const ImVec2 backup_cursor_pos = window->DC.CursorPos;
10239
0
    window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y);
10240
0
    tab_bar->BarRect.Min.x += tab_list_popup_button_width;
10241
10242
0
    ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];
10243
0
    arrow_col.w *= 0.5f;
10244
0
    PushStyleColor(ImGuiCol_Text, arrow_col);
10245
0
    PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
10246
0
    bool open = BeginCombo("##v", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest);
10247
0
    PopStyleColor(2);
10248
10249
0
    ImGuiTabItem* tab_to_select = NULL;
10250
0
    if (open)
10251
0
    {
10252
0
        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)
10253
0
        {
10254
0
            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];
10255
0
            if (tab->Flags & ImGuiTabItemFlags_Button)
10256
0
                continue;
10257
10258
0
            const char* tab_name = TabBarGetTabName(tab_bar, tab);
10259
0
            if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))
10260
0
                tab_to_select = tab;
10261
0
        }
10262
0
        EndCombo();
10263
0
    }
10264
10265
0
    window->DC.CursorPos = backup_cursor_pos;
10266
0
    return tab_to_select;
10267
0
}
10268
10269
//-------------------------------------------------------------------------
10270
// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.
10271
//-------------------------------------------------------------------------
10272
// - BeginTabItem()
10273
// - EndTabItem()
10274
// - TabItemButton()
10275
// - TabItemEx() [Internal]
10276
// - SetTabItemClosed()
10277
// - TabItemCalcSize() [Internal]
10278
// - TabItemBackground() [Internal]
10279
// - TabItemLabelAndCloseButton() [Internal]
10280
//-------------------------------------------------------------------------
10281
10282
bool    ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags)
10283
0
{
10284
0
    ImGuiContext& g = *GImGui;
10285
0
    ImGuiWindow* window = g.CurrentWindow;
10286
0
    if (window->SkipItems)
10287
0
        return false;
10288
10289
0
    ImGuiTabBar* tab_bar = g.CurrentTabBar;
10290
0
    if (tab_bar == NULL)
10291
0
    {
10292
0
        IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!");
10293
0
        return false;
10294
0
    }
10295
0
    IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead!
10296
10297
0
    bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);
10298
0
    if (ret && !(flags & ImGuiTabItemFlags_NoPushId))
10299
0
    {
10300
0
        ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
10301
0
        PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label)
10302
0
    }
10303
0
    return ret;
10304
0
}
10305
10306
void    ImGui::EndTabItem()
10307
0
{
10308
0
    ImGuiContext& g = *GImGui;
10309
0
    ImGuiWindow* window = g.CurrentWindow;
10310
0
    if (window->SkipItems)
10311
0
        return;
10312
10313
0
    ImGuiTabBar* tab_bar = g.CurrentTabBar;
10314
0
    if (tab_bar == NULL)
10315
0
    {
10316
0
        IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!");
10317
0
        return;
10318
0
    }
10319
0
    IM_ASSERT(tab_bar->LastTabItemIdx >= 0);
10320
0
    ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];
10321
0
    if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))
10322
0
        PopID();
10323
0
}
10324
10325
bool    ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags)
10326
0
{
10327
0
    ImGuiContext& g = *GImGui;
10328
0
    ImGuiWindow* window = g.CurrentWindow;
10329
0
    if (window->SkipItems)
10330
0
        return false;
10331
10332
0
    ImGuiTabBar* tab_bar = g.CurrentTabBar;
10333
0
    if (tab_bar == NULL)
10334
0
    {
10335
0
        IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!");
10336
0
        return false;
10337
0
    }
10338
0
    return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL);
10339
0
}
10340
10341
void    ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width)
10342
0
{
10343
0
    ImGuiContext& g = *GImGui;
10344
0
    ImGuiWindow* window = g.CurrentWindow;
10345
0
    if (window->SkipItems)
10346
0
        return;
10347
10348
0
    ImGuiTabBar* tab_bar = g.CurrentTabBar;
10349
0
    if (tab_bar == NULL)
10350
0
    {
10351
0
        IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!");
10352
0
        return;
10353
0
    }
10354
0
    SetNextItemWidth(width);
10355
0
    TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL);
10356
0
}
10357
10358
bool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window)
10359
0
{
10360
    // Layout whole tab bar if not already done
10361
0
    ImGuiContext& g = *GImGui;
10362
0
    if (tab_bar->WantLayout)
10363
0
    {
10364
0
        ImGuiNextItemData backup_next_item_data = g.NextItemData;
10365
0
        TabBarLayout(tab_bar);
10366
0
        g.NextItemData = backup_next_item_data;
10367
0
    }
10368
0
    ImGuiWindow* window = g.CurrentWindow;
10369
0
    if (window->SkipItems)
10370
0
        return false;
10371
10372
0
    const ImGuiStyle& style = g.Style;
10373
0
    const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window);
10374
10375
    // If the user called us with *p_open == false, we early out and don't render.
10376
    // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.
10377
0
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
10378
0
    if (p_open && !*p_open)
10379
0
    {
10380
0
        ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);
10381
0
        return false;
10382
0
    }
10383
10384
0
    IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button));
10385
0
    IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing
10386
10387
    // Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented)
10388
0
    if (flags & ImGuiTabItemFlags_NoCloseButton)
10389
0
        p_open = NULL;
10390
0
    else if (p_open == NULL)
10391
0
        flags |= ImGuiTabItemFlags_NoCloseButton;
10392
10393
    // Acquire tab data
10394
0
    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id);
10395
0
    bool tab_is_new = false;
10396
0
    if (tab == NULL)
10397
0
    {
10398
0
        tab_bar->Tabs.push_back(ImGuiTabItem());
10399
0
        tab = &tab_bar->Tabs.back();
10400
0
        tab->ID = id;
10401
0
        tab_bar->TabsAddedNew = tab_is_new = true;
10402
0
    }
10403
0
    tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab);
10404
10405
    // Calculate tab contents size
10406
0
    ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));
10407
0
    tab->RequestedWidth = -1.0f;
10408
0
    if (g.NextItemData.HasFlags & ImGuiNextItemDataFlags_HasWidth)
10409
0
        size.x = tab->RequestedWidth = g.NextItemData.Width;
10410
0
    if (tab_is_new)
10411
0
        tab->Width = ImMax(1.0f, size.x);
10412
0
    tab->ContentWidth = size.x;
10413
0
    tab->BeginOrder = tab_bar->TabsActiveCount++;
10414
10415
0
    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);
10416
0
    const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;
10417
0
    const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);
10418
0
    const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument);
10419
0
    const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;
10420
0
    tab->LastFrameVisible = g.FrameCount;
10421
0
    tab->Flags = flags;
10422
10423
    // Append name _WITH_ the zero-terminator
10424
0
    if (docked_window != NULL)
10425
0
    {
10426
0
        IM_ASSERT(docked_window == NULL); // master branch only
10427
0
    }
10428
0
    else
10429
0
    {
10430
0
        tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
10431
0
        tab_bar->TabsNames.append(label, label + ImStrlen(label) + 1);
10432
0
    }
10433
10434
    // Update selected tab
10435
0
    if (!is_tab_button)
10436
0
    {
10437
0
        if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)
10438
0
            if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)
10439
0
                TabBarQueueFocus(tab_bar, tab); // New tabs gets activated
10440
0
        if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar
10441
0
            TabBarQueueFocus(tab_bar, tab);
10442
0
    }
10443
10444
    // Lock visibility
10445
    // (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!)
10446
0
    bool tab_contents_visible = (tab_bar->VisibleTabId == id);
10447
0
    if (tab_contents_visible)
10448
0
        tab_bar->VisibleTabWasSubmitted = true;
10449
10450
    // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches
10451
0
    if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing)
10452
0
        if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))
10453
0
            tab_contents_visible = true;
10454
10455
    // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted
10456
    // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.
10457
0
    if (tab_appearing && (!tab_bar_appearing || tab_is_new))
10458
0
    {
10459
0
        ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);
10460
0
        if (is_tab_button)
10461
0
            return false;
10462
0
        return tab_contents_visible;
10463
0
    }
10464
10465
0
    if (tab_bar->SelectedTabId == id)
10466
0
        tab->LastFrameSelected = g.FrameCount;
10467
10468
    // Backup current layout position
10469
0
    const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;
10470
10471
    // Layout
10472
0
    const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
10473
0
    size.x = tab->Width;
10474
0
    if (is_central_section)
10475
0
        window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
10476
0
    else
10477
0
        window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
10478
0
    ImVec2 pos = window->DC.CursorPos;
10479
0
    ImRect bb(pos, pos + size);
10480
10481
    // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)
10482
0
    const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);
10483
0
    if (want_clip_rect)
10484
0
        PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);
10485
10486
0
    ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;
10487
0
    ItemSize(bb.GetSize(), style.FramePadding.y);
10488
0
    window->DC.CursorMaxPos = backup_cursor_max_pos;
10489
10490
0
    if (!ItemAdd(bb, id))
10491
0
    {
10492
0
        if (want_clip_rect)
10493
0
            PopClipRect();
10494
0
        window->DC.CursorPos = backup_main_cursor_pos;
10495
0
        return tab_contents_visible;
10496
0
    }
10497
10498
    // Click to Select a tab
10499
    // Allow the close button to overlap
10500
0
    ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap);
10501
0
    if (g.DragDropActive)
10502
0
        button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
10503
0
    bool hovered, held, pressed;
10504
0
    if (flags & ImGuiTabItemFlags_Invisible)
10505
0
        hovered = held = pressed = false;
10506
0
    else
10507
0
        pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
10508
0
    if (pressed && !is_tab_button)
10509
0
        TabBarQueueFocus(tab_bar, tab);
10510
10511
    // Drag and drop: re-order tabs
10512
0
    if (held && !tab_appearing && IsMouseDragging(0))
10513
0
    {
10514
0
        if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))
10515
0
        {
10516
            // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x
10517
0
            if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)
10518
0
            {
10519
0
                TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);
10520
0
            }
10521
0
            else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)
10522
0
            {
10523
0
                TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);
10524
0
            }
10525
0
        }
10526
0
    }
10527
10528
#if 0
10529
    if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth)
10530
    {
10531
        // Enlarge tab display when hovering
10532
        bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));
10533
        display_draw_list = GetForegroundDrawList(window);
10534
        TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));
10535
    }
10536
#endif
10537
10538
    // Render tab shape
10539
0
    const bool is_visible = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) && !(flags & ImGuiTabItemFlags_Invisible);
10540
0
    if (is_visible)
10541
0
    {
10542
0
        ImDrawList* display_draw_list = window->DrawList;
10543
0
        const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed));
10544
0
        TabItemBackground(display_draw_list, bb, flags, tab_col);
10545
0
        if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f)
10546
0
        {
10547
            // Might be moved to TabItemBackground() ?
10548
0
            ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale);
10549
0
            ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale);
10550
0
            ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline);
10551
0
            if (style.TabRounding > 0.0f)
10552
0
            {
10553
0
                float rounding = style.TabRounding;
10554
0
                display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9);
10555
0
                display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11);
10556
0
                display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize);
10557
0
            }
10558
0
            else
10559
0
            {
10560
0
                display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize);
10561
0
            }
10562
0
        }
10563
0
        RenderNavCursor(bb, id);
10564
10565
        // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.
10566
0
        const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
10567
0
        if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button)
10568
0
            TabBarQueueFocus(tab_bar, tab);
10569
10570
0
        if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)
10571
0
            flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;
10572
10573
        // Render tab label, process close button
10574
0
        const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0;
10575
0
        bool just_closed;
10576
0
        bool text_clipped;
10577
0
        TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);
10578
0
        if (just_closed && p_open != NULL)
10579
0
        {
10580
0
            *p_open = false;
10581
0
            TabBarCloseTab(tab_bar, tab);
10582
0
        }
10583
10584
        // Tooltip
10585
        // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok)
10586
        // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores)
10587
        // FIXME: This is a mess.
10588
        // FIXME: We may want disabled tab to still display the tooltip?
10589
0
        if (text_clipped && g.HoveredId == id && !held)
10590
0
            if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))
10591
0
                SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label);
10592
0
    }
10593
10594
    // Restore main window position so user can draw there
10595
0
    if (want_clip_rect)
10596
0
        PopClipRect();
10597
0
    window->DC.CursorPos = backup_main_cursor_pos;
10598
10599
0
    IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected
10600
0
    if (is_tab_button)
10601
0
        return pressed;
10602
0
    return tab_contents_visible;
10603
0
}
10604
10605
// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.
10606
// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar().
10607
// Tabs closed by the close button will automatically be flagged to avoid this issue.
10608
void    ImGui::SetTabItemClosed(const char* label)
10609
0
{
10610
0
    ImGuiContext& g = *GImGui;
10611
0
    bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode);
10612
0
    if (is_within_manual_tab_bar)
10613
0
    {
10614
0
        ImGuiTabBar* tab_bar = g.CurrentTabBar;
10615
0
        ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL);
10616
0
        if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))
10617
0
            tab->WantClose = true; // Will be processed by next call to TabBarLayout()
10618
0
    }
10619
0
}
10620
10621
ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker)
10622
0
{
10623
0
    ImGuiContext& g = *GImGui;
10624
0
    ImVec2 label_size = CalcTextSize(label, NULL, true);
10625
0
    ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);
10626
0
    if (has_close_button_or_unsaved_marker)
10627
0
        size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.
10628
0
    else
10629
0
        size.x += g.Style.FramePadding.x + 1.0f;
10630
0
    return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);
10631
0
}
10632
10633
ImVec2 ImGui::TabItemCalcSize(ImGuiWindow*)
10634
0
{
10635
0
    IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch.
10636
0
    return ImVec2(0.0f, 0.0f);
10637
0
}
10638
10639
void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)
10640
0
{
10641
    // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it.
10642
0
    ImGuiContext& g = *GImGui;
10643
0
    const float width = bb.GetWidth();
10644
0
    IM_UNUSED(flags);
10645
0
    IM_ASSERT(width > 0.0f);
10646
0
    const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));
10647
0
    const float y1 = bb.Min.y + 1.0f;
10648
0
    const float y2 = bb.Max.y - g.Style.TabBarBorderSize;
10649
0
    draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
10650
0
    draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
10651
0
    draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
10652
0
    draw_list->PathLineTo(ImVec2(bb.Max.x, y2));
10653
0
    draw_list->PathFillConvex(col);
10654
0
    if (g.Style.TabBorderSize > 0.0f)
10655
0
    {
10656
0
        draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));
10657
0
        draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9);
10658
0
        draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12);
10659
0
        draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2));
10660
0
        draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize);
10661
0
    }
10662
0
}
10663
10664
// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic
10665
// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter.
10666
void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped)
10667
0
{
10668
0
    ImGuiContext& g = *GImGui;
10669
0
    ImVec2 label_size = CalcTextSize(label, NULL, true);
10670
10671
0
    if (out_just_closed)
10672
0
        *out_just_closed = false;
10673
0
    if (out_text_clipped)
10674
0
        *out_text_clipped = false;
10675
10676
0
    if (bb.GetWidth() <= 1.0f)
10677
0
        return;
10678
10679
    // In Style V2 we'll have full override of all colors per state (e.g. focused, selected)
10680
    // But right now if you want to alter text color of tabs this is what you need to do.
10681
#if 0
10682
    const float backup_alpha = g.Style.Alpha;
10683
    if (!is_contents_visible)
10684
        g.Style.Alpha *= 0.7f;
10685
#endif
10686
10687
    // Render text label (with clipping + alpha gradient) + unsaved marker
10688
0
    ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
10689
10690
    // Return clipped state ignoring the close button
10691
0
    if (out_text_clipped)
10692
0
    {
10693
0
        *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x;
10694
        //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
10695
0
    }
10696
10697
0
    const float button_sz = g.FontSize;
10698
0
    const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y);
10699
10700
    // Close Button & Unsaved Marker
10701
    // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()
10702
    //  'hovered' will be true when hovering the Tab but NOT when hovering the close button
10703
    //  'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button
10704
    //  'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false
10705
0
    bool close_button_pressed = false;
10706
0
    bool close_button_visible = false;
10707
0
    bool is_hovered = g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id; // Any interaction account for this too.
10708
10709
0
    if (close_button_id != 0)
10710
0
    {
10711
0
        if (is_contents_visible)
10712
0
            close_button_visible = (g.Style.TabCloseButtonMinWidthSelected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthSelected));
10713
0
        else
10714
0
            close_button_visible = (g.Style.TabCloseButtonMinWidthUnselected < 0.0f) ? true : (is_hovered && bb.GetWidth() >= ImMax(button_sz, g.Style.TabCloseButtonMinWidthUnselected));
10715
0
    }
10716
10717
    // When tabs/document is unsaved, the unsaved marker takes priority over the close button.
10718
0
    const bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x) && (!close_button_visible || !is_hovered);
10719
0
    if (unsaved_marker_visible)
10720
0
    {
10721
0
        ImVec2 bullet_pos = button_pos + ImVec2(button_sz, button_sz) * 0.5f;
10722
0
        RenderBullet(draw_list, bullet_pos, GetColorU32(ImGuiCol_UnsavedMarker));
10723
0
    }
10724
0
    else if (close_button_visible)
10725
0
    {
10726
0
        ImGuiLastItemData last_item_backup = g.LastItemData;
10727
0
        if (CloseButton(close_button_id, button_pos))
10728
0
            close_button_pressed = true;
10729
0
        g.LastItemData = last_item_backup;
10730
10731
        // Close with middle mouse button
10732
0
        if (is_hovered && !(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))
10733
0
            close_button_pressed = true;
10734
0
    }
10735
10736
    // This is all rather complicated
10737
    // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
10738
    // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..
10739
0
    float ellipsis_max_x = text_ellipsis_clip_bb.Max.x;
10740
0
    if (close_button_visible || unsaved_marker_visible)
10741
0
    {
10742
0
        const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f;
10743
0
        if (visible_without_hover)
10744
0
        {
10745
0
            text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f;
10746
0
            ellipsis_max_x -= button_sz * 0.90f;
10747
0
        }
10748
0
        else
10749
0
        {
10750
0
            text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f;
10751
0
        }
10752
0
    }
10753
0
    LogSetNextTextDecoration("/", "\\");
10754
0
    RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);
10755
10756
#if 0
10757
    if (!is_contents_visible)
10758
        g.Style.Alpha = backup_alpha;
10759
#endif
10760
10761
0
    if (out_just_closed)
10762
0
        *out_just_closed = close_button_pressed;
10763
0
}
10764
10765
10766
#endif // #ifndef IMGUI_DISABLE
/Users/kiltum/projects/zxcpp/lib/imgui/imstb_rectpack.h
Line
Count
Source
1
// [DEAR IMGUI]
2
// This is a slightly modified version of stb_rect_pack.h 1.01.
3
// Grep for [DEAR IMGUI] to find the changes.
4
// 
5
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
6
// Sean Barrett 2014
7
//
8
// Useful for e.g. packing rectangular textures into an atlas.
9
// Does not do rotation.
10
//
11
// Before #including,
12
//
13
//    #define STB_RECT_PACK_IMPLEMENTATION
14
//
15
// in the file that you want to have the implementation.
16
//
17
// Not necessarily the awesomest packing method, but better than
18
// the totally naive one in stb_truetype (which is primarily what
19
// this is meant to replace).
20
//
21
// Has only had a few tests run, may have issues.
22
//
23
// More docs to come.
24
//
25
// No memory allocations; uses qsort() and assert() from stdlib.
26
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
27
//
28
// This library currently uses the Skyline Bottom-Left algorithm.
29
//
30
// Please note: better rectangle packers are welcome! Please
31
// implement them to the same API, but with a different init
32
// function.
33
//
34
// Credits
35
//
36
//  Library
37
//    Sean Barrett
38
//  Minor features
39
//    Martins Mozeiko
40
//    github:IntellectualKitty
41
//
42
//  Bugfixes / warning fixes
43
//    Jeremy Jaussaud
44
//    Fabian Giesen
45
//
46
// Version history:
47
//
48
//     1.01  (2021-07-11)  always use large rect mode, expose STBRP__MAXVAL in public section
49
//     1.00  (2019-02-25)  avoid small space waste; gracefully fail too-wide rectangles
50
//     0.99  (2019-02-07)  warning fixes
51
//     0.11  (2017-03-03)  return packing success/fail result
52
//     0.10  (2016-10-25)  remove cast-away-const to avoid warnings
53
//     0.09  (2016-08-27)  fix compiler warnings
54
//     0.08  (2015-09-13)  really fix bug with empty rects (w=0 or h=0)
55
//     0.07  (2015-09-13)  fix bug with empty rects (w=0 or h=0)
56
//     0.06  (2015-04-15)  added STBRP_SORT to allow replacing qsort
57
//     0.05:  added STBRP_ASSERT to allow replacing assert
58
//     0.04:  fixed minor bug in STBRP_LARGE_RECTS support
59
//     0.01:  initial release
60
//
61
// LICENSE
62
//
63
//   See end of file for license information.
64
65
//////////////////////////////////////////////////////////////////////////////
66
//
67
//       INCLUDE SECTION
68
//
69
70
#ifndef STB_INCLUDE_STB_RECT_PACK_H
71
#define STB_INCLUDE_STB_RECT_PACK_H
72
73
#define STB_RECT_PACK_VERSION  1
74
75
#ifdef STBRP_STATIC
76
#define STBRP_DEF static
77
#else
78
#define STBRP_DEF extern
79
#endif
80
81
#ifdef __cplusplus
82
extern "C" {
83
#endif
84
85
typedef struct stbrp_context stbrp_context;
86
typedef struct stbrp_node    stbrp_node;
87
typedef struct stbrp_rect    stbrp_rect;
88
89
typedef int            stbrp_coord;
90
91
44
#define STBRP__MAXVAL  0x7fffffff
92
// Mostly for internal use, but this is the maximum supported coordinate value.
93
94
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
95
// Assign packed locations to rectangles. The rectangles are of type
96
// 'stbrp_rect' defined below, stored in the array 'rects', and there
97
// are 'num_rects' many of them.
98
//
99
// Rectangles which are successfully packed have the 'was_packed' flag
100
// set to a non-zero value and 'x' and 'y' store the minimum location
101
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
102
// if you imagine y increasing downwards). Rectangles which do not fit
103
// have the 'was_packed' flag set to 0.
104
//
105
// You should not try to access the 'rects' array from another thread
106
// while this function is running, as the function temporarily reorders
107
// the array while it executes.
108
//
109
// To pack into another rectangle, you need to call stbrp_init_target
110
// again. To continue packing into the same rectangle, you can call
111
// this function again. Calling this multiple times with multiple rect
112
// arrays will probably produce worse packing results than calling it
113
// a single time with the full rectangle array, but the option is
114
// available.
115
//
116
// The function returns 1 if all of the rectangles were successfully
117
// packed and 0 otherwise.
118
119
struct stbrp_rect
120
{
121
   // reserved for your use:
122
   int            id;
123
124
   // input:
125
   stbrp_coord    w, h;
126
127
   // output:
128
   stbrp_coord    x, y;
129
   int            was_packed;  // non-zero if valid packing
130
131
}; // 16 bytes, nominally
132
133
134
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
135
// Initialize a rectangle packer to:
136
//    pack a rectangle that is 'width' by 'height' in dimensions
137
//    using temporary storage provided by the array 'nodes', which is 'num_nodes' long
138
//
139
// You must call this function every time you start packing into a new target.
140
//
141
// There is no "shutdown" function. The 'nodes' memory must stay valid for
142
// the following stbrp_pack_rects() call (or calls), but can be freed after
143
// the call (or calls) finish.
144
//
145
// Note: to guarantee best results, either:
146
//       1. make sure 'num_nodes' >= 'width'
147
//   or  2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
148
//
149
// If you don't do either of the above things, widths will be quantized to multiples
150
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
151
//
152
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
153
// may run out of temporary storage and be unable to pack some rectangles.
154
155
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
156
// Optionally call this function after init but before doing any packing to
157
// change the handling of the out-of-temp-memory scenario, described above.
158
// If you call init again, this will be reset to the default (false).
159
160
161
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
162
// Optionally select which packing heuristic the library should use. Different
163
// heuristics will produce better/worse results for different data sets.
164
// If you call init again, this will be reset to the default.
165
166
enum
167
{
168
   STBRP_HEURISTIC_Skyline_default=0,
169
   STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
170
   STBRP_HEURISTIC_Skyline_BF_sortHeight
171
};
172
173
174
//////////////////////////////////////////////////////////////////////////////
175
//
176
// the details of the following structures don't matter to you, but they must
177
// be visible so you can handle the memory allocations for them
178
179
struct stbrp_node
180
{
181
   stbrp_coord  x,y;
182
   stbrp_node  *next;
183
};
184
185
struct stbrp_context
186
{
187
   int width;
188
   int height;
189
   int align;
190
   int init_mode;
191
   int heuristic;
192
   int num_nodes;
193
   stbrp_node *active_head;
194
   stbrp_node *free_head;
195
   stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
196
};
197
198
#ifdef __cplusplus
199
}
200
#endif
201
202
#endif
203
204
//////////////////////////////////////////////////////////////////////////////
205
//
206
//     IMPLEMENTATION SECTION
207
//
208
209
#ifdef STB_RECT_PACK_IMPLEMENTATION
210
#ifndef STBRP_SORT
211
#include <stdlib.h>
212
#define STBRP_SORT qsort
213
#endif
214
215
#ifndef STBRP_ASSERT
216
#include <assert.h>
217
#define STBRP_ASSERT assert
218
#endif
219
220
#ifdef _MSC_VER
221
#define STBRP__NOTUSED(v)  (void)(v)
222
#define STBRP__CDECL       __cdecl
223
#else
224
253
#define STBRP__NOTUSED(v)  (void)sizeof(v)
225
#define STBRP__CDECL
226
#endif
227
228
enum
229
{
230
   STBRP__INIT_skyline = 1
231
};
232
233
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
234
0
{
235
0
   switch (context->init_mode) {
236
0
      case STBRP__INIT_skyline:
237
0
         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
238
0
         context->heuristic = heuristic;
239
0
         break;
240
0
      default:
241
0
         STBRP_ASSERT(0);
242
0
   }
243
0
}
244
245
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
246
1
{
247
1
   if (allow_out_of_mem)
248
      // if it's ok to run out of memory, then don't bother aligning them;
249
      // this gives better packing, but may fail due to OOM (even though
250
      // the rectangles easily fit). @TODO a smarter approach would be to only
251
      // quantize once we've hit OOM, then we could get rid of this parameter.
252
0
      context->align = 1;
253
1
   else {
254
      // if it's not ok to run out of memory, then quantize the widths
255
      // so that num_nodes is always enough nodes.
256
      //
257
      // I.e. num_nodes * align >= width
258
      //                  align >= width / num_nodes
259
      //                  align = ceil(width/num_nodes)
260
261
1
      context->align = (context->width + context->num_nodes-1) / context->num_nodes;
262
1
   }
263
1
}
264
265
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
266
1
{
267
1
   int i;
268
269
256
   for (i=0; i < num_nodes-1; ++i)
270
255
      nodes[i].next = &nodes[i+1];
271
1
   nodes[i].next = NULL;
272
1
   context->init_mode = STBRP__INIT_skyline;
273
1
   context->heuristic = STBRP_HEURISTIC_Skyline_default;
274
1
   context->free_head = &nodes[0];
275
1
   context->active_head = &context->extra[0];
276
1
   context->width = width;
277
1
   context->height = height;
278
1
   context->num_nodes = num_nodes;
279
1
   stbrp_setup_allow_out_of_mem(context, 0);
280
281
   // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
282
1
   context->extra[0].x = 0;
283
1
   context->extra[0].y = 0;
284
1
   context->extra[0].next = &context->extra[1];
285
1
   context->extra[1].x = (stbrp_coord) width;
286
1
   context->extra[1].y = (1<<30);
287
1
   context->extra[1].next = NULL;
288
1
}
289
290
// find minimum y position if it starts at x1
291
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
292
253
{
293
253
   stbrp_node *node = first;
294
253
   int x1 = x0 + width;
295
253
   int min_y, visited_width, waste_area;
296
297
253
   STBRP__NOTUSED(c);
298
299
253
   STBRP_ASSERT(first->x <= x0);
300
301
   #if 0
302
   // skip in case we're past the node
303
   while (node->next->x <= x0)
304
      ++node;
305
   #else
306
253
   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
307
253
   #endif
308
309
253
   STBRP_ASSERT(node->x <= x0);
310
311
253
   min_y = 0;
312
253
   waste_area = 0;
313
253
   visited_width = 0;
314
559
   while (node->x < x1) {
315
306
      if (node->y > min_y) {
316
         // raise min_y higher.
317
         // we've accounted for all waste up to min_y,
318
         // but we'll now add more waste for everything we've visted
319
242
         waste_area += visited_width * (node->y - min_y);
320
242
         min_y = node->y;
321
         // the first time through, visited_width might be reduced
322
242
         if (node->x < x0)
323
0
            visited_width += node->next->x - x0;
324
242
         else
325
242
            visited_width += node->next->x - node->x;
326
242
      } else {
327
         // add waste area
328
64
         int under_width = node->next->x - node->x;
329
64
         if (under_width + visited_width > width)
330
52
            under_width = width - visited_width;
331
64
         waste_area += under_width * (min_y - node->y);
332
64
         visited_width += under_width;
333
64
      }
334
306
      node = node->next;
335
306
   }
336
337
253
   *pwaste = waste_area;
338
253
   return min_y;
339
253
}
340
341
typedef struct
342
{
343
   int x,y;
344
   stbrp_node **prev_link;
345
} stbrp__findresult;
346
347
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
348
22
{
349
22
   int best_waste = (1<<30), best_x, best_y = (1 << 30);
350
22
   stbrp__findresult fr;
351
22
   stbrp_node **prev, *node, *tail, **best = NULL;
352
353
   // align to multiple of c->align
354
22
   width = (width + c->align - 1);
355
22
   width -= width % c->align;
356
22
   STBRP_ASSERT(width % c->align == 0);
357
358
   // if it can't possibly fit, bail immediately
359
22
   if (width > c->width || height > c->height) {
360
0
      fr.prev_link = NULL;
361
0
      fr.x = fr.y = 0;
362
0
      return fr;
363
0
   }
364
365
22
   node = c->active_head;
366
22
   prev = &c->active_head;
367
275
   while (node->x + width <= c->width) {
368
253
      int y,waste;
369
253
      y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
370
253
      if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
371
         // bottom left
372
253
         if (y < best_y) {
373
99
            best_y = y;
374
99
            best = prev;
375
99
         }
376
253
      } else {
377
         // best-fit
378
0
         if (y + height <= c->height) {
379
            // can only use it if it first vertically
380
0
            if (y < best_y || (y == best_y && waste < best_waste)) {
381
0
               best_y = y;
382
0
               best_waste = waste;
383
0
               best = prev;
384
0
            }
385
0
         }
386
0
      }
387
253
      prev = &node->next;
388
253
      node = node->next;
389
253
   }
390
391
22
   best_x = (best == NULL) ? 0 : (*best)->x;
392
393
   // if doing best-fit (BF), we also have to try aligning right edge to each node position
394
   //
395
   // e.g, if fitting
396
   //
397
   //     ____________________
398
   //    |____________________|
399
   //
400
   //            into
401
   //
402
   //   |                         |
403
   //   |             ____________|
404
   //   |____________|
405
   //
406
   // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
407
   //
408
   // This makes BF take about 2x the time
409
410
22
   if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
411
0
      tail = c->active_head;
412
0
      node = c->active_head;
413
0
      prev = &c->active_head;
414
      // find first node that's admissible
415
0
      while (tail->x < width)
416
0
         tail = tail->next;
417
0
      while (tail) {
418
0
         int xpos = tail->x - width;
419
0
         int y,waste;
420
0
         STBRP_ASSERT(xpos >= 0);
421
         // find the left position that matches this
422
0
         while (node->next->x <= xpos) {
423
0
            prev = &node->next;
424
0
            node = node->next;
425
0
         }
426
0
         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
427
0
         y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
428
0
         if (y + height <= c->height) {
429
0
            if (y <= best_y) {
430
0
               if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
431
0
                  best_x = xpos;
432
                  //STBRP_ASSERT(y <= best_y); [DEAR IMGUI]
433
0
                  best_y = y;
434
0
                  best_waste = waste;
435
0
                  best = prev;
436
0
               }
437
0
            }
438
0
         }
439
0
         tail = tail->next;
440
0
      }
441
0
   }
442
443
22
   fr.prev_link = best;
444
22
   fr.x = best_x;
445
22
   fr.y = best_y;
446
22
   return fr;
447
22
}
448
449
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
450
22
{
451
   // find best position according to heuristic
452
22
   stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
453
22
   stbrp_node *node, *cur;
454
455
   // bail if:
456
   //    1. it failed
457
   //    2. the best node doesn't fit (we don't always check this)
458
   //    3. we're out of memory
459
22
   if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
460
0
      res.prev_link = NULL;
461
0
      return res;
462
0
   }
463
464
   // on success, create new node
465
22
   node = context->free_head;
466
22
   node->x = (stbrp_coord) res.x;
467
22
   node->y = (stbrp_coord) (res.y + height);
468
469
22
   context->free_head = node->next;
470
471
   // insert the new node into the right starting point, and
472
   // let 'cur' point to the remaining nodes needing to be
473
   // stiched back in
474
475
22
   cur = *res.prev_link;
476
22
   if (cur->x < res.x) {
477
      // preserve the existing one, so start testing with the next one
478
0
      stbrp_node *next = cur->next;
479
0
      cur->next = node;
480
0
      cur = next;
481
22
   } else {
482
22
      *res.prev_link = node;
483
22
   }
484
485
   // from here, traverse cur and free the nodes, until we get to one
486
   // that shouldn't be freed
487
22
   while (cur->next && cur->next->x <= res.x + width) {
488
0
      stbrp_node *next = cur->next;
489
      // move the current node to the free list
490
0
      cur->next = context->free_head;
491
0
      context->free_head = cur;
492
0
      cur = next;
493
0
   }
494
495
   // stitch the list back in
496
22
   node->next = cur;
497
498
22
   if (cur->x < res.x + width)
499
22
      cur->x = (stbrp_coord) (res.x + width);
500
501
#ifdef _DEBUG
502
   cur = context->active_head;
503
   while (cur->x < context->width) {
504
      STBRP_ASSERT(cur->x < cur->next->x);
505
      cur = cur->next;
506
   }
507
   STBRP_ASSERT(cur->next == NULL);
508
509
   {
510
      int count=0;
511
      cur = context->active_head;
512
      while (cur) {
513
         cur = cur->next;
514
         ++count;
515
      }
516
      cur = context->free_head;
517
      while (cur) {
518
         cur = cur->next;
519
         ++count;
520
      }
521
      STBRP_ASSERT(count == context->num_nodes+2);
522
   }
523
#endif
524
525
22
   return res;
526
22
}
527
528
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
529
0
{
530
0
   const stbrp_rect *p = (const stbrp_rect *) a;
531
0
   const stbrp_rect *q = (const stbrp_rect *) b;
532
0
   if (p->h > q->h)
533
0
      return -1;
534
0
   if (p->h < q->h)
535
0
      return  1;
536
0
   return (p->w > q->w) ? -1 : (p->w < q->w);
537
0
}
538
539
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
540
0
{
541
0
   const stbrp_rect *p = (const stbrp_rect *) a;
542
0
   const stbrp_rect *q = (const stbrp_rect *) b;
543
0
   return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
544
0
}
545
546
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
547
22
{
548
22
   int i, all_rects_packed = 1;
549
550
   // we use the 'was_packed' field internally to allow sorting/unsorting
551
44
   for (i=0; i < num_rects; ++i) {
552
22
      rects[i].was_packed = i;
553
22
   }
554
555
   // sort according to heuristic
556
22
   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
557
558
44
   for (i=0; i < num_rects; ++i) {
559
22
      if (rects[i].w == 0 || rects[i].h == 0) {
560
0
         rects[i].x = rects[i].y = 0;  // empty rect needs no space
561
22
      } else {
562
22
         stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
563
22
         if (fr.prev_link) {
564
22
            rects[i].x = (stbrp_coord) fr.x;
565
22
            rects[i].y = (stbrp_coord) fr.y;
566
22
         } else {
567
0
            rects[i].x = rects[i].y = STBRP__MAXVAL;
568
0
         }
569
22
      }
570
22
   }
571
572
   // unsort
573
22
   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
574
575
   // set was_packed flags and all_rects_packed status
576
44
   for (i=0; i < num_rects; ++i) {
577
22
      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
578
22
      if (!rects[i].was_packed)
579
0
         all_rects_packed = 0;
580
22
   }
581
582
   // return the all_rects_packed status
583
22
   return all_rects_packed;
584
22
}
585
#endif
586
587
/*
588
------------------------------------------------------------------------------
589
This software is available under 2 licenses -- choose whichever you prefer.
590
------------------------------------------------------------------------------
591
ALTERNATIVE A - MIT License
592
Copyright (c) 2017 Sean Barrett
593
Permission is hereby granted, free of charge, to any person obtaining a copy of
594
this software and associated documentation files (the "Software"), to deal in
595
the Software without restriction, including without limitation the rights to
596
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
597
of the Software, and to permit persons to whom the Software is furnished to do
598
so, subject to the following conditions:
599
The above copyright notice and this permission notice shall be included in all
600
copies or substantial portions of the Software.
601
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
602
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
603
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
604
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
605
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
606
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
607
SOFTWARE.
608
------------------------------------------------------------------------------
609
ALTERNATIVE B - Public Domain (www.unlicense.org)
610
This is free and unencumbered software released into the public domain.
611
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
612
software, either in source code form or as a compiled binary, for any purpose,
613
commercial or non-commercial, and by any means.
614
In jurisdictions that recognize copyright laws, the author or authors of this
615
software dedicate any and all copyright interest in the software to the public
616
domain. We make this dedication for the benefit of the public at large and to
617
the detriment of our heirs and successors. We intend this dedication to be an
618
overt act of relinquishment in perpetuity of all present and future rights to
619
this software under copyright law.
620
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
621
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
622
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
623
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
624
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
625
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
626
------------------------------------------------------------------------------
627
*/
/Users/kiltum/projects/zxcpp/lib/imgui/imstb_textedit.h
Line
Count
Source
1
// [DEAR IMGUI]
2
// This is a slightly modified version of stb_textedit.h 1.14.
3
// Those changes would need to be pushed into nothings/stb:
4
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
5
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
6
// - Added name to struct or it may be forward declared in our code.
7
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
8
// Grep for [DEAR IMGUI] to find the changes.
9
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
10
11
// stb_textedit.h - v1.14  - public domain - Sean Barrett
12
// Development of this library was sponsored by RAD Game Tools
13
//
14
// This C header file implements the guts of a multi-line text-editing
15
// widget; you implement display, word-wrapping, and low-level string
16
// insertion/deletion, and stb_textedit will map user inputs into
17
// insertions & deletions, plus updates to the cursor position,
18
// selection state, and undo state.
19
//
20
// It is intended for use in games and other systems that need to build
21
// their own custom widgets and which do not have heavy text-editing
22
// requirements (this library is not recommended for use for editing large
23
// texts, as its performance does not scale and it has limited undo).
24
//
25
// Non-trivial behaviors are modelled after Windows text controls.
26
//
27
//
28
// LICENSE
29
//
30
// See end of file for license information.
31
//
32
//
33
// DEPENDENCIES
34
//
35
// Uses the C runtime function 'memmove', which you can override
36
// by defining IMSTB_TEXTEDIT_memmove before the implementation.
37
// Uses no other functions. Performs no runtime allocations.
38
//
39
//
40
// VERSION HISTORY
41
//
42
//   1.14 (2021-07-11) page up/down, various fixes
43
//   1.13 (2019-02-07) fix bug in undo size management
44
//   1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
45
//   1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
46
//   1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
47
//   1.9  (2016-08-27) customizable move-by-word
48
//   1.8  (2016-04-02) better keyboard handling when mouse button is down
49
//   1.7  (2015-09-13) change y range handling in case baseline is non-0
50
//   1.6  (2015-04-15) allow STB_TEXTEDIT_memmove
51
//   1.5  (2014-09-10) add support for secondary keys for OS X
52
//   1.4  (2014-08-17) fix signed/unsigned warnings
53
//   1.3  (2014-06-19) fix mouse clicking to round to nearest char boundary
54
//   1.2  (2014-05-27) fix some RAD types that had crept into the new code
55
//   1.1  (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE )
56
//   1.0  (2012-07-26) improve documentation, initial public release
57
//   0.3  (2012-02-24) bugfixes, single-line mode; insert mode
58
//   0.2  (2011-11-28) fixes to undo/redo
59
//   0.1  (2010-07-08) initial version
60
//
61
// ADDITIONAL CONTRIBUTORS
62
//
63
//   Ulf Winklemann: move-by-word in 1.1
64
//   Fabian Giesen: secondary key inputs in 1.5
65
//   Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6
66
//   Louis Schnellbach: page up/down in 1.14
67
//
68
//   Bugfixes:
69
//      Scott Graham
70
//      Daniel Keller
71
//      Omar Cornut
72
//      Dan Thompson
73
//
74
// USAGE
75
//
76
// This file behaves differently depending on what symbols you define
77
// before including it.
78
//
79
//
80
// Header-file mode:
81
//
82
//   If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this,
83
//   it will operate in "header file" mode. In this mode, it declares a
84
//   single public symbol, STB_TexteditState, which encapsulates the current
85
//   state of a text widget (except for the string, which you will store
86
//   separately).
87
//
88
//   To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a
89
//   primitive type that defines a single character (e.g. char, wchar_t, etc).
90
//
91
//   To save space or increase undo-ability, you can optionally define the
92
//   following things that are used by the undo system:
93
//
94
//      STB_TEXTEDIT_POSITIONTYPE         small int type encoding a valid cursor position
95
//      STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow
96
//      STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer
97
//
98
//   If you don't define these, they are set to permissive types and
99
//   moderate sizes. The undo system does no memory allocations, so
100
//   it grows STB_TexteditState by the worst-case storage which is (in bytes):
101
//
102
//        [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT
103
//      +          sizeof(STB_TEXTEDIT_CHARTYPE)      * STB_TEXTEDIT_UNDOCHARCOUNT
104
//
105
//
106
// Implementation mode:
107
//
108
//   If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it
109
//   will compile the implementation of the text edit widget, depending
110
//   on a large number of symbols which must be defined before the include.
111
//
112
//   The implementation is defined only as static functions. You will then
113
//   need to provide your own APIs in the same file which will access the
114
//   static functions.
115
//
116
//   The basic concept is that you provide a "string" object which
117
//   behaves like an array of characters. stb_textedit uses indices to
118
//   refer to positions in the string, implicitly representing positions
119
//   in the displayed textedit. This is true for both plain text and
120
//   rich text; even with rich text stb_truetype interacts with your
121
//   code as if there was an array of all the displayed characters.
122
//
123
// Symbols that must be the same in header-file and implementation mode:
124
//
125
//     STB_TEXTEDIT_CHARTYPE             the character type
126
//     STB_TEXTEDIT_POSITIONTYPE         small type that is a valid cursor position
127
//     STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow
128
//     STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer
129
//
130
// Symbols you must define for implementation mode:
131
//
132
//    STB_TEXTEDIT_STRING               the type of object representing a string being edited,
133
//                                      typically this is a wrapper object with other data you need
134
//
135
//    STB_TEXTEDIT_STRINGLEN(obj)       the length of the string (ideally O(1))
136
//    STB_TEXTEDIT_LAYOUTROW(&r,obj,n)  returns the results of laying out a line of characters
137
//                                        starting from character #n (see discussion below)
138
//    STB_TEXTEDIT_GETWIDTH(obj,n,i)    returns the pixel delta from the xpos of the i'th character
139
//                                        to the xpos of the i+1'th char for a line of characters
140
//                                        starting at character #n (i.e. accounts for kerning
141
//                                        with previous char)
142
//    STB_TEXTEDIT_KEYTOTEXT(k)         maps a keyboard input to an insertable character
143
//                                        (return type is int, -1 means not valid to insert)
144
//                                        (not supported if you want to use UTF-8, see below)
145
//    STB_TEXTEDIT_GETCHAR(obj,i)       returns the i'th character of obj, 0-based
146
//    STB_TEXTEDIT_NEWLINE              the character returned by _GETCHAR() we recognize
147
//                                        as manually wordwrapping for end-of-line positioning
148
//
149
//    STB_TEXTEDIT_DELETECHARS(obj,i,n)      delete n characters starting at i
150
//    STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n)   insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)
151
//
152
//    STB_TEXTEDIT_K_SHIFT       a power of two that is or'd in to a keyboard input to represent the shift key
153
//
154
//    STB_TEXTEDIT_K_LEFT        keyboard input to move cursor left
155
//    STB_TEXTEDIT_K_RIGHT       keyboard input to move cursor right
156
//    STB_TEXTEDIT_K_UP          keyboard input to move cursor up
157
//    STB_TEXTEDIT_K_DOWN        keyboard input to move cursor down
158
//    STB_TEXTEDIT_K_PGUP        keyboard input to move cursor up a page
159
//    STB_TEXTEDIT_K_PGDOWN      keyboard input to move cursor down a page
160
//    STB_TEXTEDIT_K_LINESTART   keyboard input to move cursor to start of line  // e.g. HOME
161
//    STB_TEXTEDIT_K_LINEEND     keyboard input to move cursor to end of line    // e.g. END
162
//    STB_TEXTEDIT_K_TEXTSTART   keyboard input to move cursor to start of text  // e.g. ctrl-HOME
163
//    STB_TEXTEDIT_K_TEXTEND     keyboard input to move cursor to end of text    // e.g. ctrl-END
164
//    STB_TEXTEDIT_K_DELETE      keyboard input to delete selection or character under cursor
165
//    STB_TEXTEDIT_K_BACKSPACE   keyboard input to delete selection or character left of cursor
166
//    STB_TEXTEDIT_K_UNDO        keyboard input to perform undo
167
//    STB_TEXTEDIT_K_REDO        keyboard input to perform redo
168
//
169
// Optional:
170
//    STB_TEXTEDIT_K_INSERT              keyboard input to toggle insert mode
171
//    STB_TEXTEDIT_IS_SPACE(ch)          true if character is whitespace (e.g. 'isspace'),
172
//                                          required for default WORDLEFT/WORDRIGHT handlers
173
//    STB_TEXTEDIT_MOVEWORDLEFT(obj,i)   custom handler for WORDLEFT, returns index to move cursor to
174
//    STB_TEXTEDIT_MOVEWORDRIGHT(obj,i)  custom handler for WORDRIGHT, returns index to move cursor to
175
//    STB_TEXTEDIT_K_WORDLEFT            keyboard input to move cursor left one word // e.g. ctrl-LEFT
176
//    STB_TEXTEDIT_K_WORDRIGHT           keyboard input to move cursor right one word // e.g. ctrl-RIGHT
177
//    STB_TEXTEDIT_K_LINESTART2          secondary keyboard input to move cursor to start of line
178
//    STB_TEXTEDIT_K_LINEEND2            secondary keyboard input to move cursor to end of line
179
//    STB_TEXTEDIT_K_TEXTSTART2          secondary keyboard input to move cursor to start of text
180
//    STB_TEXTEDIT_K_TEXTEND2            secondary keyboard input to move cursor to end of text
181
//
182
// To support UTF-8:
183
//
184
//    STB_TEXTEDIT_GETPREVCHARINDEX      returns index of previous character
185
//    STB_TEXTEDIT_GETNEXTCHARINDEX      returns index of next character
186
//    Do NOT define STB_TEXTEDIT_KEYTOTEXT.
187
//    Instead, call stb_textedit_text() directly for text contents.
188
//
189
// Keyboard input must be encoded as a single integer value; e.g. a character code
190
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
191
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
192
// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.
193
//
194
// You can encode other things, such as CONTROL or ALT, in additional bits, and
195
// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example,
196
// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN
197
// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit,
198
// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the
199
// API below. The control keys will only match WM_KEYDOWN events because of the
200
// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN
201
// bit so it only decodes WM_CHAR events.
202
//
203
// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed
204
// row of characters assuming they start on the i'th character--the width and
205
// the height and the number of characters consumed. This allows this library
206
// to traverse the entire layout incrementally. You need to compute word-wrapping
207
// here.
208
//
209
// Each textfield keeps its own insert mode state, which is not how normal
210
// applications work. To keep an app-wide insert mode, update/copy the
211
// "insert_mode" field of STB_TexteditState before/after calling API functions.
212
//
213
// API
214
//
215
//    void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)
216
//
217
//    void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
218
//    void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
219
//    int  stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
220
//    int  stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
221
//    void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
222
//    void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
223
//
224
//    Each of these functions potentially updates the string and updates the
225
//    state.
226
//
227
//      initialize_state:
228
//          set the textedit state to a known good default state when initially
229
//          constructing the textedit.
230
//
231
//      click:
232
//          call this with the mouse x,y on a mouse down; it will update the cursor
233
//          and reset the selection start/end to the cursor point. the x,y must
234
//          be relative to the text widget, with (0,0) being the top left.
235
//
236
//      drag:
237
//          call this with the mouse x,y on a mouse drag/up; it will update the
238
//          cursor and the selection end point
239
//
240
//      cut:
241
//          call this to delete the current selection; returns true if there was
242
//          one. you should FIRST copy the current selection to the system paste buffer.
243
//          (To copy, just copy the current selection out of the string yourself.)
244
//
245
//      paste:
246
//          call this to paste text at the current cursor point or over the current
247
//          selection if there is one.
248
//
249
//      key:
250
//          call this for keyboard inputs sent to the textfield. you can use it
251
//          for "key down" events or for "translated" key events. if you need to
252
//          do both (as in Win32), or distinguish Unicode characters from control
253
//          inputs, set a high bit to distinguish the two; then you can define the
254
//          various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
255
//          set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
256
//          clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
257
//          anything other type you want before including.
258
//          if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
259
//          transformed into text and stb_textedit_text() is automatically called.
260
//
261
//      text: (added 2025)
262
//          call this to directly send text input the textfield, which is required
263
//          for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
264
//          cannot infer text length.
265
//
266
//
267
//   When rendering, you can read the cursor position and selection state from
268
//   the STB_TexteditState.
269
//
270
//
271
// Notes:
272
//
273
// This is designed to be usable in IMGUI, so it allows for the possibility of
274
// running in an IMGUI that has NOT cached the multi-line layout. For this
275
// reason, it provides an interface that is compatible with computing the
276
// layout incrementally--we try to make sure we make as few passes through
277
// as possible. (For example, to locate the mouse pointer in the text, we
278
// could define functions that return the X and Y positions of characters
279
// and binary search Y and then X, but if we're doing dynamic layout this
280
// will run the layout algorithm many times, so instead we manually search
281
// forward in one pass. Similar logic applies to e.g. up-arrow and
282
// down-arrow movement.)
283
//
284
// If it's run in a widget that *has* cached the layout, then this is less
285
// efficient, but it's not horrible on modern computers. But you wouldn't
286
// want to edit million-line files with it.
287
288
289
////////////////////////////////////////////////////////////////////////////
290
////////////////////////////////////////////////////////////////////////////
291
////
292
////   Header-file mode
293
////
294
////
295
296
#ifndef INCLUDE_IMSTB_TEXTEDIT_H
297
#define INCLUDE_IMSTB_TEXTEDIT_H
298
299
////////////////////////////////////////////////////////////////////////
300
//
301
//     STB_TexteditState
302
//
303
// Definition of STB_TexteditState which you should store
304
// per-textfield; it includes cursor position, selection state,
305
// and undo state.
306
//
307
308
#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT
309
#define IMSTB_TEXTEDIT_UNDOSTATECOUNT   99
310
#endif
311
#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT
312
#define IMSTB_TEXTEDIT_UNDOCHARCOUNT   999
313
#endif
314
#ifndef IMSTB_TEXTEDIT_CHARTYPE
315
#define IMSTB_TEXTEDIT_CHARTYPE        int
316
#endif
317
#ifndef IMSTB_TEXTEDIT_POSITIONTYPE
318
#define IMSTB_TEXTEDIT_POSITIONTYPE    int
319
#endif
320
321
typedef struct
322
{
323
   // private data
324
   IMSTB_TEXTEDIT_POSITIONTYPE  where;
325
   IMSTB_TEXTEDIT_POSITIONTYPE  insert_length;
326
   IMSTB_TEXTEDIT_POSITIONTYPE  delete_length;
327
   int                        char_storage;
328
} StbUndoRecord;
329
330
typedef struct
331
{
332
   // private data
333
   StbUndoRecord          undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT];
334
   IMSTB_TEXTEDIT_CHARTYPE  undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT];
335
   short undo_point, redo_point;
336
   int undo_char_point, redo_char_point;
337
} StbUndoState;
338
339
typedef struct STB_TexteditState
340
{
341
   /////////////////////
342
   //
343
   // public data
344
   //
345
346
   int cursor;
347
   // position of the text cursor within the string
348
349
   int select_start;          // selection start point
350
   int select_end;
351
   // selection start and end point in characters; if equal, no selection.
352
   // note that start may be less than or greater than end (e.g. when
353
   // dragging the mouse, start is where the initial click was, and you
354
   // can drag in either direction)
355
356
   unsigned char insert_mode;
357
   // each textfield keeps its own insert mode state. to keep an app-wide
358
   // insert mode, copy this value in/out of the app state
359
360
   int row_count_per_page;
361
   // page size in number of row.
362
   // this value MUST be set to >0 for pageup or pagedown in multilines documents.
363
364
   /////////////////////
365
   //
366
   // private data
367
   //
368
   unsigned char cursor_at_end_of_line; // not implemented yet
369
   unsigned char initialized;
370
   unsigned char has_preferred_x;
371
   unsigned char single_line;
372
   unsigned char padding1, padding2, padding3;
373
   float preferred_x; // this determines where the cursor up/down tries to seek to along x
374
   StbUndoState undostate;
375
} STB_TexteditState;
376
377
378
////////////////////////////////////////////////////////////////////////
379
//
380
//     StbTexteditRow
381
//
382
// Result of layout query, used by stb_textedit to determine where
383
// the text in each row is.
384
385
// result of layout query
386
typedef struct
387
{
388
   float x0,x1;             // starting x location, end x location (allows for align=right, etc)
389
   float baseline_y_delta;  // position of baseline relative to previous row's baseline
390
   float ymin,ymax;         // height of row above and below baseline
391
   int num_chars;
392
} StbTexteditRow;
393
#endif //INCLUDE_IMSTB_TEXTEDIT_H
394
395
396
////////////////////////////////////////////////////////////////////////////
397
////////////////////////////////////////////////////////////////////////////
398
////
399
////   Implementation mode
400
////
401
////
402
403
404
// implementation isn't include-guarded, since it might have indirectly
405
// included just the "header" portion
406
#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION
407
408
#ifndef IMSTB_TEXTEDIT_memmove
409
#include <string.h>
410
#define IMSTB_TEXTEDIT_memmove memmove
411
#endif
412
413
// [DEAR IMGUI]
414
// Functions must be implemented for UTF8 support
415
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
416
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
417
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
418
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
419
#endif
420
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
421
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
422
#endif
423
424
/////////////////////////////////////////////////////////////////////////////
425
//
426
//      Mouse input handling
427
//
428
429
// traverse the layout to locate the nearest character to a display position
430
static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y, int* out_side_on_line)
431
0
{
432
0
   StbTexteditRow r;
433
0
   int n = STB_TEXTEDIT_STRINGLEN(str);
434
0
   float base_y = 0, prev_x;
435
0
   int i=0, k;
436
437
0
   r.x0 = r.x1 = 0;
438
0
   r.ymin = r.ymax = 0;
439
0
   r.num_chars = 0;
440
0
   *out_side_on_line = 0;
441
442
   // search rows to find one that straddles 'y'
443
0
   while (i < n) {
444
0
      STB_TEXTEDIT_LAYOUTROW(&r, str, i);
445
0
      if (r.num_chars <= 0)
446
0
         return n;
447
448
0
      if (i==0 && y < base_y + r.ymin)
449
0
         return 0;
450
451
0
      if (y < base_y + r.ymax)
452
0
         break;
453
454
0
      i += r.num_chars;
455
0
      base_y += r.baseline_y_delta;
456
0
   }
457
458
   // below all text, return 'after' last character
459
0
   if (i >= n)
460
0
   {
461
0
      *out_side_on_line = 1;
462
0
      return n;
463
0
   }
464
465
   // check if it's before the beginning of the line
466
0
   if (x < r.x0)
467
0
      return i;
468
469
   // check if it's before the end of the line
470
0
   if (x < r.x1) {
471
      // search characters in row for one that straddles 'x'
472
0
      prev_x = r.x0;
473
0
      for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
474
0
         float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
475
0
         if (x < prev_x+w) {
476
0
            *out_side_on_line = (k == 0) ? 0 : 1;
477
0
            if (x < prev_x+w/2)
478
0
               return k+i;
479
0
            else
480
0
               return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
481
0
         }
482
0
         prev_x += w;
483
0
      }
484
      // shouldn't happen, but if it does, fall through to end-of-line case
485
0
   }
486
487
   // if the last character is a newline, return that. otherwise return 'after' the last character
488
0
   *out_side_on_line = 1;
489
0
   if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)
490
0
      return i+r.num_chars-1;
491
0
   else
492
0
      return i+r.num_chars;
493
0
}
494
495
// API click: on mouse down, move the cursor to the clicked location, and reset the selection
496
static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
497
0
{
498
   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
499
   // goes off the top or bottom of the text
500
0
   int side_on_line;
501
0
   if( state->single_line )
502
0
   {
503
0
      StbTexteditRow r;
504
0
      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
505
0
      y = r.ymin;
506
0
   }
507
508
0
   state->cursor = stb_text_locate_coord(str, x, y, &side_on_line);
509
0
   state->select_start = state->cursor;
510
0
   state->select_end = state->cursor;
511
0
   state->has_preferred_x = 0;
512
0
   str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
513
0
}
514
515
// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location
516
static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)
517
0
{
518
0
   int p = 0;
519
0
   int side_on_line;
520
521
   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse
522
   // goes off the top or bottom of the text
523
0
   if( state->single_line )
524
0
   {
525
0
      StbTexteditRow r;
526
0
      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
527
0
      y = r.ymin;
528
0
   }
529
530
0
   if (state->select_start == state->select_end)
531
0
      state->select_start = state->cursor;
532
533
0
   p = stb_text_locate_coord(str, x, y, &side_on_line);
534
0
   state->cursor = state->select_end = p;
535
0
   str->LastMoveDirectionLR = (ImS8)(side_on_line ? ImGuiDir_Right : ImGuiDir_Left);
536
0
}
537
538
/////////////////////////////////////////////////////////////////////////////
539
//
540
//      Keyboard input handling
541
//
542
543
// forward declarations
544
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
545
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state);
546
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);
547
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);
548
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);
549
550
typedef struct
551
{
552
   float x,y;    // position of n'th character
553
   float height; // height of line
554
   int first_char, length; // first char of row, and length
555
   int prev_first;  // first char of previous row
556
} StbFindState;
557
558
// find the x/y location of a character, and remember info about the previous row in
559
// case we get a move-up event (for page up, we'll have to rescan)
560
static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line)
561
0
{
562
0
   StbTexteditRow r;
563
0
   int prev_start = 0;
564
0
   int z = STB_TEXTEDIT_STRINGLEN(str);
565
0
   int i=0, first;
566
567
0
   if (n == z && single_line) {
568
      // special case if it's at the end (may not be needed?)
569
0
      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
570
0
      find->y = 0;
571
0
      find->first_char = 0;
572
0
      find->length = z;
573
0
      find->height = r.ymax - r.ymin;
574
0
      find->x = r.x1;
575
0
      return;
576
0
   }
577
578
   // search rows to find the one that straddles character n
579
0
   find->y = 0;
580
581
0
   for(;;) {
582
0
      STB_TEXTEDIT_LAYOUTROW(&r, str, i);
583
0
      if (n < i + r.num_chars)
584
0
         break;
585
0
      if (str->LastMoveDirectionLR == ImGuiDir_Right && str->Stb->cursor > 0 && str->Stb->cursor == i + r.num_chars && STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] Wrapping point handling
586
0
         break;
587
0
      if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE)  // [DEAR IMGUI] special handling for last line
588
0
         break;   // [DEAR IMGUI]
589
0
      prev_start = i;
590
0
      i += r.num_chars;
591
0
      find->y += r.baseline_y_delta;
592
0
      if (i == z) // [DEAR IMGUI]
593
0
      {
594
0
         r.num_chars = 0; // [DEAR IMGUI]
595
0
         break;   // [DEAR IMGUI]
596
0
      }
597
0
   }
598
599
0
   find->first_char = first = i;
600
0
   find->length = r.num_chars;
601
0
   find->height = r.ymax - r.ymin;
602
0
   find->prev_first = prev_start;
603
604
   // now scan to find xpos
605
0
   find->x = r.x0;
606
0
   for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
607
0
      find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
608
0
}
609
610
0
#define STB_TEXT_HAS_SELECTION(s)   ((s)->select_start != (s)->select_end)
611
612
// make the selection/cursor state valid if client altered the string
613
static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
614
0
{
615
0
   int n = STB_TEXTEDIT_STRINGLEN(str);
616
0
   if (STB_TEXT_HAS_SELECTION(state)) {
617
0
      if (state->select_start > n) state->select_start = n;
618
0
      if (state->select_end   > n) state->select_end = n;
619
      // if clamping forced them to be equal, move the cursor to match
620
0
      if (state->select_start == state->select_end)
621
0
         state->cursor = state->select_start;
622
0
   }
623
0
   if (state->cursor > n) state->cursor = n;
624
0
}
625
626
// delete characters while updating undo
627
static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)
628
0
{
629
0
   stb_text_makeundo_delete(str, state, where, len);
630
0
   STB_TEXTEDIT_DELETECHARS(str, where, len);
631
0
   state->has_preferred_x = 0;
632
0
}
633
634
// delete the section
635
static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
636
0
{
637
0
   stb_textedit_clamp(str, state);
638
0
   if (STB_TEXT_HAS_SELECTION(state)) {
639
0
      if (state->select_start < state->select_end) {
640
0
         stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start);
641
0
         state->select_end = state->cursor = state->select_start;
642
0
      } else {
643
0
         stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end);
644
0
         state->select_start = state->cursor = state->select_end;
645
0
      }
646
0
      state->has_preferred_x = 0;
647
0
   }
648
0
}
649
650
// canoncialize the selection so start <= end
651
static void stb_textedit_sortselection(STB_TexteditState *state)
652
0
{
653
0
   if (state->select_end < state->select_start) {
654
0
      int temp = state->select_end;
655
0
      state->select_end = state->select_start;
656
0
      state->select_start = temp;
657
0
   }
658
0
}
659
660
// move cursor to first character of selection
661
static void stb_textedit_move_to_first(STB_TexteditState *state)
662
0
{
663
0
   if (STB_TEXT_HAS_SELECTION(state)) {
664
0
      stb_textedit_sortselection(state);
665
0
      state->cursor = state->select_start;
666
0
      state->select_end = state->select_start;
667
0
      state->has_preferred_x = 0;
668
0
   }
669
0
}
670
671
// move cursor to last character of selection
672
static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
673
0
{
674
0
   if (STB_TEXT_HAS_SELECTION(state)) {
675
0
      stb_textedit_sortselection(state);
676
0
      stb_textedit_clamp(str, state);
677
0
      state->cursor = state->select_end;
678
0
      state->select_start = state->select_end;
679
0
      state->has_preferred_x = 0;
680
0
   }
681
0
}
682
683
// [DEAR IMGUI] Extracted this function so we can more easily add support for word-wrapping.
684
#ifndef STB_TEXTEDIT_MOVELINESTART
685
static int stb_textedit_move_line_start(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
686
{
687
   if (state->single_line)
688
      return 0;
689
   while (cursor > 0) {
690
      int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, cursor);
691
      if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
692
         break;
693
      cursor = prev;
694
   }
695
   return cursor;
696
}
697
#define STB_TEXTEDIT_MOVELINESTART stb_textedit_move_line_start
698
#endif
699
#ifndef STB_TEXTEDIT_MOVELINEEND
700
static int stb_textedit_move_line_end(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int cursor)
701
{
702
   int n = STB_TEXTEDIT_STRINGLEN(str);
703
   if (state->single_line)
704
      return n;
705
   while (cursor < n && STB_TEXTEDIT_GETCHAR(str, cursor) != STB_TEXTEDIT_NEWLINE)
706
      cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, cursor);
707
   return cursor;
708
}
709
#define STB_TEXTEDIT_MOVELINEEND stb_textedit_move_line_end
710
#endif
711
712
#ifdef STB_TEXTEDIT_IS_SPACE
713
static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
714
{
715
   return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;
716
}
717
718
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
719
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
720
{
721
   c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
722
   while (c >= 0 && !is_word_boundary(str, c))
723
      c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
724
725
   if( c < 0 )
726
      c = 0;
727
728
   return c;
729
}
730
#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous
731
#endif
732
733
#ifndef STB_TEXTEDIT_MOVEWORDRIGHT
734
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
735
{
736
   const int len = STB_TEXTEDIT_STRINGLEN(str);
737
   c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
738
   while( c < len && !is_word_boundary( str, c ) )
739
      c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
740
741
   if( c > len )
742
      c = len;
743
744
   return c;
745
}
746
#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next
747
#endif
748
749
#endif
750
751
// update selection and cursor to match each other
752
static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)
753
0
{
754
0
   if (!STB_TEXT_HAS_SELECTION(state))
755
0
      state->select_start = state->select_end = state->cursor;
756
0
   else
757
0
      state->cursor = state->select_end;
758
0
}
759
760
// API cut: delete selection
761
static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
762
0
{
763
0
   if (STB_TEXT_HAS_SELECTION(state)) {
764
0
      stb_textedit_delete_selection(str,state); // implicitly clamps
765
0
      state->has_preferred_x = 0;
766
0
      return 1;
767
0
   }
768
0
   return 0;
769
0
}
770
771
// API paste: replace existing selection with passed-in text
772
static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len)
773
0
{
774
   // if there's a selection, the paste should delete it
775
0
   stb_textedit_clamp(str, state);
776
0
   stb_textedit_delete_selection(str,state);
777
   // try to insert the characters
778
0
   if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {
779
0
      stb_text_makeundo_insert(state, state->cursor, len);
780
0
      state->cursor += len;
781
0
      state->has_preferred_x = 0;
782
0
      return 1;
783
0
   }
784
   // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details)
785
0
   return 0;
786
0
}
787
788
#ifndef STB_TEXTEDIT_KEYTYPE
789
#define STB_TEXTEDIT_KEYTYPE int
790
#endif
791
792
// API key: process text input
793
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
794
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
795
0
{
796
   // can't add newline in single-line mode
797
0
   if (text[0] == '\n' && state->single_line)
798
0
      return;
799
800
0
   if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
801
0
      stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
802
0
      STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
803
0
      if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
804
0
         state->cursor += text_len;
805
0
         state->has_preferred_x = 0;
806
0
      }
807
0
   } else {
808
0
      stb_textedit_delete_selection(str, state); // implicitly clamps
809
0
      if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
810
0
         stb_text_makeundo_insert(state, state->cursor, text_len);
811
0
         state->cursor += text_len;
812
0
         state->has_preferred_x = 0;
813
0
      }
814
0
   }
815
0
}
816
817
// API key: process a keyboard input
818
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
819
0
{
820
0
retry:
821
0
   switch (key) {
822
0
      default: {
823
#ifdef STB_TEXTEDIT_KEYTOTEXT
824
         // This is not suitable for UTF-8 support.
825
         int c = STB_TEXTEDIT_KEYTOTEXT(key);
826
         if (c > 0) {
827
            IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
828
            stb_textedit_text(str, state, &ch, 1);
829
         }
830
#endif
831
0
         break;
832
0
      }
833
834
#ifdef STB_TEXTEDIT_K_INSERT
835
      case STB_TEXTEDIT_K_INSERT:
836
         state->insert_mode = !state->insert_mode;
837
         break;
838
#endif
839
840
0
      case STB_TEXTEDIT_K_UNDO:
841
0
         stb_text_undo(str, state);
842
0
         state->has_preferred_x = 0;
843
0
         break;
844
845
0
      case STB_TEXTEDIT_K_REDO:
846
0
         stb_text_redo(str, state);
847
0
         state->has_preferred_x = 0;
848
0
         break;
849
850
0
      case STB_TEXTEDIT_K_LEFT:
851
         // if currently there's a selection, move cursor to start of selection
852
0
         if (STB_TEXT_HAS_SELECTION(state))
853
0
            stb_textedit_move_to_first(state);
854
0
         else
855
0
            if (state->cursor > 0)
856
0
               state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
857
0
         state->has_preferred_x = 0;
858
0
         break;
859
860
0
      case STB_TEXTEDIT_K_RIGHT:
861
         // if currently there's a selection, move cursor to end of selection
862
0
         if (STB_TEXT_HAS_SELECTION(state))
863
0
            stb_textedit_move_to_last(str, state);
864
0
         else
865
0
            state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
866
0
         stb_textedit_clamp(str, state);
867
0
         state->has_preferred_x = 0;
868
0
         break;
869
870
0
      case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT:
871
0
         stb_textedit_clamp(str, state);
872
0
         stb_textedit_prep_selection_at_cursor(state);
873
         // move selection left
874
0
         if (state->select_end > 0)
875
0
            state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
876
0
         state->cursor = state->select_end;
877
0
         state->has_preferred_x = 0;
878
0
         break;
879
880
0
#ifdef STB_TEXTEDIT_MOVEWORDLEFT
881
0
      case STB_TEXTEDIT_K_WORDLEFT:
882
0
         if (STB_TEXT_HAS_SELECTION(state))
883
0
            stb_textedit_move_to_first(state);
884
0
         else {
885
0
            state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
886
0
            stb_textedit_clamp( str, state );
887
0
         }
888
0
         break;
889
890
0
      case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT:
891
0
         if( !STB_TEXT_HAS_SELECTION( state ) )
892
0
            stb_textedit_prep_selection_at_cursor(state);
893
894
0
         state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);
895
0
         state->select_end = state->cursor;
896
897
0
         stb_textedit_clamp( str, state );
898
0
         break;
899
0
#endif
900
901
0
#ifdef STB_TEXTEDIT_MOVEWORDRIGHT
902
0
      case STB_TEXTEDIT_K_WORDRIGHT:
903
0
         if (STB_TEXT_HAS_SELECTION(state))
904
0
            stb_textedit_move_to_last(str, state);
905
0
         else {
906
0
            state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
907
0
            stb_textedit_clamp( str, state );
908
0
         }
909
0
         break;
910
911
0
      case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:
912
0
         if( !STB_TEXT_HAS_SELECTION( state ) )
913
0
            stb_textedit_prep_selection_at_cursor(state);
914
915
0
         state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);
916
0
         state->select_end = state->cursor;
917
918
0
         stb_textedit_clamp( str, state );
919
0
         break;
920
0
#endif
921
922
0
      case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
923
0
         stb_textedit_prep_selection_at_cursor(state);
924
         // move selection right
925
0
         state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
926
0
         stb_textedit_clamp(str, state);
927
0
         state->cursor = state->select_end;
928
0
         state->has_preferred_x = 0;
929
0
         break;
930
931
0
      case STB_TEXTEDIT_K_DOWN:
932
0
      case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT:
933
0
      case STB_TEXTEDIT_K_PGDOWN:
934
0
      case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: {
935
0
         StbFindState find;
936
0
         StbTexteditRow row;
937
0
         int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
938
0
         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN;
939
0
         int row_count = is_page ? state->row_count_per_page : 1;
940
941
0
         if (!is_page && state->single_line) {
942
            // on windows, up&down in single-line behave like left&right
943
0
            key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);
944
0
            goto retry;
945
0
         }
946
947
0
         if (sel)
948
0
            stb_textedit_prep_selection_at_cursor(state);
949
0
         else if (STB_TEXT_HAS_SELECTION(state))
950
0
            stb_textedit_move_to_last(str, state);
951
952
         // compute current position of cursor point
953
0
         stb_textedit_clamp(str, state);
954
0
         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
955
956
0
         for (j = 0; j < row_count; ++j) {
957
0
            float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
958
0
            int start = find.first_char + find.length;
959
960
0
            if (find.length == 0)
961
0
               break;
962
963
            // [DEAR IMGUI]
964
            // going down while being on the last line shouldn't bring us to that line end
965
            //if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)
966
            //   break;
967
968
            // now find character position down a row
969
0
            state->cursor = start;
970
0
            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
971
0
            x = row.x0;
972
0
            for (i=0; i < row.num_chars; ) {
973
0
               float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
974
0
               int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
975
0
               #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
976
0
               if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
977
0
                  break;
978
0
               #endif
979
0
               x += dx;
980
0
               if (x > goal_x)
981
0
                  break;
982
0
               i += next - state->cursor;
983
0
               state->cursor = next;
984
0
            }
985
0
            stb_textedit_clamp(str, state);
986
987
0
            if (state->cursor == find.first_char + find.length)
988
0
               str->LastMoveDirectionLR = ImGuiDir_Left;
989
0
            state->has_preferred_x = 1;
990
0
            state->preferred_x = goal_x;
991
992
0
            if (sel)
993
0
               state->select_end = state->cursor;
994
995
            // go to next line
996
0
            find.first_char = find.first_char + find.length;
997
0
            find.length = row.num_chars;
998
0
         }
999
0
         break;
1000
0
      }
1001
1002
0
      case STB_TEXTEDIT_K_UP:
1003
0
      case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT:
1004
0
      case STB_TEXTEDIT_K_PGUP:
1005
0
      case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: {
1006
0
         StbFindState find;
1007
0
         StbTexteditRow row;
1008
0
         int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;
1009
0
         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP;
1010
0
         int row_count = is_page ? state->row_count_per_page : 1;
1011
1012
0
         if (!is_page && state->single_line) {
1013
            // on windows, up&down become left&right
1014
0
            key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);
1015
0
            goto retry;
1016
0
         }
1017
1018
0
         if (sel)
1019
0
            stb_textedit_prep_selection_at_cursor(state);
1020
0
         else if (STB_TEXT_HAS_SELECTION(state))
1021
0
            stb_textedit_move_to_first(state);
1022
1023
         // compute current position of cursor point
1024
0
         stb_textedit_clamp(str, state);
1025
0
         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);
1026
1027
0
         for (j = 0; j < row_count; ++j) {
1028
0
            float  x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;
1029
1030
            // can only go up if there's a previous row
1031
0
            if (find.prev_first == find.first_char)
1032
0
               break;
1033
1034
            // now find character position up a row
1035
0
            state->cursor = find.prev_first;
1036
0
            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
1037
0
            x = row.x0;
1038
0
            for (i=0; i < row.num_chars; ) {
1039
0
               float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
1040
0
               int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
1041
0
               #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
1042
0
               if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
1043
0
                  break;
1044
0
               #endif
1045
0
               x += dx;
1046
0
               if (x > goal_x)
1047
0
                  break;
1048
0
               i += next - state->cursor;
1049
0
               state->cursor = next;
1050
0
            }
1051
0
            stb_textedit_clamp(str, state);
1052
1053
0
            if (state->cursor == find.first_char)
1054
0
               str->LastMoveDirectionLR = ImGuiDir_Right;
1055
0
            else if (state->cursor == find.prev_first)
1056
0
               str->LastMoveDirectionLR = ImGuiDir_Left;
1057
0
            state->has_preferred_x = 1;
1058
0
            state->preferred_x = goal_x;
1059
1060
0
            if (sel)
1061
0
               state->select_end = state->cursor;
1062
1063
            // go to previous line
1064
            // (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
1065
0
            prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
1066
0
            while (prev_scan > 0)
1067
0
            {
1068
0
               int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
1069
0
               if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
1070
0
                  break;
1071
0
               prev_scan = prev;
1072
0
            }
1073
0
            find.first_char = find.prev_first;
1074
0
            find.prev_first = STB_TEXTEDIT_MOVELINESTART(str, state, prev_scan);
1075
0
         }
1076
0
         break;
1077
0
      }
1078
1079
0
      case STB_TEXTEDIT_K_DELETE:
1080
0
      case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT:
1081
0
         if (STB_TEXT_HAS_SELECTION(state))
1082
0
            stb_textedit_delete_selection(str, state);
1083
0
         else {
1084
0
            int n = STB_TEXTEDIT_STRINGLEN(str);
1085
0
            if (state->cursor < n)
1086
0
               stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
1087
0
         }
1088
0
         state->has_preferred_x = 0;
1089
0
         break;
1090
1091
0
      case STB_TEXTEDIT_K_BACKSPACE:
1092
0
      case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT:
1093
0
         if (STB_TEXT_HAS_SELECTION(state))
1094
0
            stb_textedit_delete_selection(str, state);
1095
0
         else {
1096
0
            stb_textedit_clamp(str, state);
1097
0
            if (state->cursor > 0) {
1098
0
               int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
1099
0
               stb_textedit_delete(str, state, prev, state->cursor - prev);
1100
0
               state->cursor = prev;
1101
0
            }
1102
0
         }
1103
0
         state->has_preferred_x = 0;
1104
0
         break;
1105
1106
#ifdef STB_TEXTEDIT_K_TEXTSTART2
1107
      case STB_TEXTEDIT_K_TEXTSTART2:
1108
#endif
1109
0
      case STB_TEXTEDIT_K_TEXTSTART:
1110
0
         state->cursor = state->select_start = state->select_end = 0;
1111
0
         state->has_preferred_x = 0;
1112
0
         break;
1113
1114
#ifdef STB_TEXTEDIT_K_TEXTEND2
1115
      case STB_TEXTEDIT_K_TEXTEND2:
1116
#endif
1117
0
      case STB_TEXTEDIT_K_TEXTEND:
1118
0
         state->cursor = STB_TEXTEDIT_STRINGLEN(str);
1119
0
         state->select_start = state->select_end = 0;
1120
0
         state->has_preferred_x = 0;
1121
0
         break;
1122
1123
#ifdef STB_TEXTEDIT_K_TEXTSTART2
1124
      case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT:
1125
#endif
1126
0
      case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT:
1127
0
         stb_textedit_prep_selection_at_cursor(state);
1128
0
         state->cursor = state->select_end = 0;
1129
0
         state->has_preferred_x = 0;
1130
0
         break;
1131
1132
#ifdef STB_TEXTEDIT_K_TEXTEND2
1133
      case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT:
1134
#endif
1135
0
      case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT:
1136
0
         stb_textedit_prep_selection_at_cursor(state);
1137
0
         state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str);
1138
0
         state->has_preferred_x = 0;
1139
0
         break;
1140
1141
1142
#ifdef STB_TEXTEDIT_K_LINESTART2
1143
      case STB_TEXTEDIT_K_LINESTART2:
1144
#endif
1145
0
      case STB_TEXTEDIT_K_LINESTART:
1146
0
         stb_textedit_clamp(str, state);
1147
0
         stb_textedit_move_to_first(state);
1148
0
         state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
1149
0
         state->has_preferred_x = 0;
1150
0
         break;
1151
1152
#ifdef STB_TEXTEDIT_K_LINEEND2
1153
      case STB_TEXTEDIT_K_LINEEND2:
1154
#endif
1155
0
      case STB_TEXTEDIT_K_LINEEND: {
1156
0
         stb_textedit_clamp(str, state);
1157
0
         stb_textedit_move_to_last(str, state);
1158
0
         state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
1159
0
         state->has_preferred_x = 0;
1160
0
         break;
1161
0
      }
1162
1163
#ifdef STB_TEXTEDIT_K_LINESTART2
1164
      case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:
1165
#endif
1166
0
      case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:
1167
0
         stb_textedit_clamp(str, state);
1168
0
         stb_textedit_prep_selection_at_cursor(state);
1169
0
         state->cursor = STB_TEXTEDIT_MOVELINESTART(str, state, state->cursor);
1170
0
         state->select_end = state->cursor;
1171
0
         state->has_preferred_x = 0;
1172
0
         break;
1173
1174
#ifdef STB_TEXTEDIT_K_LINEEND2
1175
      case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:
1176
#endif
1177
0
      case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {
1178
0
         stb_textedit_clamp(str, state);
1179
0
         stb_textedit_prep_selection_at_cursor(state);
1180
0
         state->cursor = STB_TEXTEDIT_MOVELINEEND(str, state, state->cursor);
1181
0
         state->select_end = state->cursor;
1182
0
         state->has_preferred_x = 0;
1183
0
         break;
1184
0
      }
1185
0
   }
1186
0
}
1187
1188
/////////////////////////////////////////////////////////////////////////////
1189
//
1190
//      Undo processing
1191
//
1192
// @OPTIMIZE: the undo/redo buffer should be circular
1193
1194
static void stb_textedit_flush_redo(StbUndoState *state)
1195
0
{
1196
0
   state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
1197
0
   state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
1198
0
}
1199
1200
// discard the oldest entry in the undo list
1201
static void stb_textedit_discard_undo(StbUndoState *state)
1202
0
{
1203
0
   if (state->undo_point > 0) {
1204
      // if the 0th undo state has characters, clean those up
1205
0
      if (state->undo_rec[0].char_storage >= 0) {
1206
0
         int n = state->undo_rec[0].insert_length, i;
1207
         // delete n characters from all other records
1208
0
         state->undo_char_point -= n;
1209
0
         IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
1210
0
         for (i=0; i < state->undo_point; ++i)
1211
0
            if (state->undo_rec[i].char_storage >= 0)
1212
0
               state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it
1213
0
      }
1214
0
      --state->undo_point;
1215
0
      IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));
1216
0
   }
1217
0
}
1218
1219
// discard the oldest entry in the redo list--it's bad if this
1220
// ever happens, but because undo & redo have to store the actual
1221
// characters in different cases, the redo character buffer can
1222
// fill up even though the undo buffer didn't
1223
static void stb_textedit_discard_redo(StbUndoState *state)
1224
0
{
1225
0
   int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1;
1226
1227
0
   if (state->redo_point <= k) {
1228
      // if the k'th undo state has characters, clean those up
1229
0
      if (state->undo_rec[k].char_storage >= 0) {
1230
0
         int n = state->undo_rec[k].insert_length, i;
1231
         // move the remaining redo character data to the end of the buffer
1232
0
         state->redo_char_point += n;
1233
0
         IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE)));
1234
         // adjust the position of all the other records to account for above memmove
1235
0
         for (i=state->redo_point; i < k; ++i)
1236
0
            if (state->undo_rec[i].char_storage >= 0)
1237
0
               state->undo_rec[i].char_storage += n;
1238
0
      }
1239
      // now move all the redo records towards the end of the buffer; the first one is at 'redo_point'
1240
      // [DEAR IMGUI]
1241
0
      size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));
1242
0
      const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;
1243
0
      const char* buf_end   = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;
1244
0
      IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);
1245
0
      IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);
1246
0
      IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);
1247
1248
      // now move redo_point to point to the new one
1249
0
      ++state->redo_point;
1250
0
   }
1251
0
}
1252
1253
static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars)
1254
0
{
1255
   // any time we create a new undo record, we discard redo
1256
0
   stb_textedit_flush_redo(state);
1257
1258
   // if we have no free records, we have to make room, by sliding the
1259
   // existing records down
1260
0
   if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
1261
0
      stb_textedit_discard_undo(state);
1262
1263
   // if the characters to store won't possibly fit in the buffer, we can't undo
1264
0
   if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
1265
0
      state->undo_point = 0;
1266
0
      state->undo_char_point = 0;
1267
0
      return NULL;
1268
0
   }
1269
1270
   // if we don't have enough free characters in the buffer, we have to make room
1271
0
   while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT)
1272
0
      stb_textedit_discard_undo(state);
1273
1274
0
   return &state->undo_rec[state->undo_point++];
1275
0
}
1276
1277
static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)
1278
0
{
1279
0
   StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);
1280
0
   if (r == NULL)
1281
0
      return NULL;
1282
1283
0
   r->where = pos;
1284
0
   r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len;
1285
0
   r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len;
1286
1287
0
   if (insert_len == 0) {
1288
0
      r->char_storage = -1;
1289
0
      return NULL;
1290
0
   } else {
1291
0
      r->char_storage = state->undo_char_point;
1292
0
      state->undo_char_point += insert_len;
1293
0
      return &state->undo_char[r->char_storage];
1294
0
   }
1295
0
}
1296
1297
static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
1298
0
{
1299
0
   StbUndoState *s = &state->undostate;
1300
0
   StbUndoRecord u, *r;
1301
0
   if (s->undo_point == 0)
1302
0
      return;
1303
1304
   // we need to do two things: apply the undo record, and create a redo record
1305
0
   u = s->undo_rec[s->undo_point-1];
1306
0
   r = &s->undo_rec[s->redo_point-1];
1307
0
   r->char_storage = -1;
1308
1309
0
   r->insert_length = u.delete_length;
1310
0
   r->delete_length = u.insert_length;
1311
0
   r->where = u.where;
1312
1313
0
   if (u.delete_length) {
1314
      // if the undo record says to delete characters, then the redo record will
1315
      // need to re-insert the characters that get deleted, so we need to store
1316
      // them.
1317
1318
      // there are three cases:
1319
      //    there's enough room to store the characters
1320
      //    characters stored for *redoing* don't leave room for redo
1321
      //    characters stored for *undoing* don't leave room for redo
1322
      // if the last is true, we have to bail
1323
1324
0
      if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) {
1325
         // the undo records take up too much character space; there's no space to store the redo characters
1326
0
         r->insert_length = 0;
1327
0
      } else {
1328
0
         int i;
1329
1330
         // there's definitely room to store the characters eventually
1331
0
         while (s->undo_char_point + u.delete_length > s->redo_char_point) {
1332
            // should never happen:
1333
0
            if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
1334
0
               return;
1335
            // there's currently not enough room, so discard a redo record
1336
0
            stb_textedit_discard_redo(s);
1337
0
         }
1338
0
         r = &s->undo_rec[s->redo_point-1];
1339
1340
0
         r->char_storage = s->redo_char_point - u.delete_length;
1341
0
         s->redo_char_point = s->redo_char_point - u.delete_length;
1342
1343
         // now save the characters
1344
0
         for (i=0; i < u.delete_length; ++i)
1345
0
            s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i);
1346
0
      }
1347
1348
      // now we can carry out the deletion
1349
0
      STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length);
1350
0
   }
1351
1352
   // check type of recorded action:
1353
0
   if (u.insert_length) {
1354
      // easy case: was a deletion, so we need to insert n characters
1355
0
      STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);
1356
0
      s->undo_char_point -= u.insert_length;
1357
0
   }
1358
1359
0
   state->cursor = u.where + u.insert_length;
1360
1361
0
   s->undo_point--;
1362
0
   s->redo_point--;
1363
0
}
1364
1365
static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state)
1366
0
{
1367
0
   StbUndoState *s = &state->undostate;
1368
0
   StbUndoRecord *u, r;
1369
0
   if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT)
1370
0
      return;
1371
1372
   // we need to do two things: apply the redo record, and create an undo record
1373
0
   u = &s->undo_rec[s->undo_point];
1374
0
   r = s->undo_rec[s->redo_point];
1375
1376
   // we KNOW there must be room for the undo record, because the redo record
1377
   // was derived from an undo record
1378
1379
0
   u->delete_length = r.insert_length;
1380
0
   u->insert_length = r.delete_length;
1381
0
   u->where = r.where;
1382
0
   u->char_storage = -1;
1383
1384
0
   if (r.delete_length) {
1385
      // the redo record requires us to delete characters, so the undo record
1386
      // needs to store the characters
1387
1388
0
      if (s->undo_char_point + u->insert_length > s->redo_char_point) {
1389
0
         u->insert_length = 0;
1390
0
         u->delete_length = 0;
1391
0
      } else {
1392
0
         int i;
1393
0
         u->char_storage = s->undo_char_point;
1394
0
         s->undo_char_point = s->undo_char_point + u->insert_length;
1395
1396
         // now save the characters
1397
0
         for (i=0; i < u->insert_length; ++i)
1398
0
            s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i);
1399
0
      }
1400
1401
0
      STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length);
1402
0
   }
1403
1404
0
   if (r.insert_length) {
1405
      // easy case: need to insert n characters
1406
0
      STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);
1407
0
      s->redo_char_point += r.insert_length;
1408
0
   }
1409
1410
0
   state->cursor = r.where + r.insert_length;
1411
1412
0
   s->undo_point++;
1413
0
   s->redo_point++;
1414
0
}
1415
1416
static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length)
1417
0
{
1418
0
   stb_text_createundo(&state->undostate, where, 0, length);
1419
0
}
1420
1421
static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)
1422
0
{
1423
0
   int i;
1424
0
   IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);
1425
0
   if (p) {
1426
0
      for (i=0; i < length; ++i)
1427
0
         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
1428
0
   }
1429
0
}
1430
1431
static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)
1432
0
{
1433
0
   int i;
1434
0
   IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);
1435
0
   if (p) {
1436
0
      for (i=0; i < old_length; ++i)
1437
0
         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);
1438
0
   }
1439
0
}
1440
1441
// reset the state to default
1442
static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line)
1443
0
{
1444
0
   state->undostate.undo_point = 0;
1445
0
   state->undostate.undo_char_point = 0;
1446
0
   state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT;
1447
0
   state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT;
1448
0
   state->select_end = state->select_start = 0;
1449
0
   state->cursor = 0;
1450
0
   state->has_preferred_x = 0;
1451
0
   state->preferred_x = 0;
1452
0
   state->cursor_at_end_of_line = 0;
1453
0
   state->initialized = 1;
1454
0
   state->single_line = (unsigned char) is_single_line;
1455
0
   state->insert_mode = 0;
1456
0
   state->row_count_per_page = 0;
1457
0
}
1458
1459
// API initialize
1460
static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)
1461
0
{
1462
0
   stb_textedit_clear_state(state, is_single_line);
1463
0
}
1464
1465
#if defined(__GNUC__) || defined(__clang__)
1466
#pragma GCC diagnostic push
1467
#pragma GCC diagnostic ignored "-Wcast-qual"
1468
#endif
1469
1470
static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len)
1471
0
{
1472
0
   return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len);
1473
0
}
1474
1475
#if defined(__GNUC__) || defined(__clang__)
1476
#pragma GCC diagnostic pop
1477
#endif
1478
1479
#endif//IMSTB_TEXTEDIT_IMPLEMENTATION
1480
1481
/*
1482
------------------------------------------------------------------------------
1483
This software is available under 2 licenses -- choose whichever you prefer.
1484
------------------------------------------------------------------------------
1485
ALTERNATIVE A - MIT License
1486
Copyright (c) 2017 Sean Barrett
1487
Permission is hereby granted, free of charge, to any person obtaining a copy of
1488
this software and associated documentation files (the "Software"), to deal in
1489
the Software without restriction, including without limitation the rights to
1490
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1491
of the Software, and to permit persons to whom the Software is furnished to do
1492
so, subject to the following conditions:
1493
The above copyright notice and this permission notice shall be included in all
1494
copies or substantial portions of the Software.
1495
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1496
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1497
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1498
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1499
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1500
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1501
SOFTWARE.
1502
------------------------------------------------------------------------------
1503
ALTERNATIVE B - Public Domain (www.unlicense.org)
1504
This is free and unencumbered software released into the public domain.
1505
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1506
software, either in source code form or as a compiled binary, for any purpose,
1507
commercial or non-commercial, and by any means.
1508
In jurisdictions that recognize copyright laws, the author or authors of this
1509
software dedicate any and all copyright interest in the software to the public
1510
domain. We make this dedication for the benefit of the public at large and to
1511
the detriment of our heirs and successors. We intend this dedication to be an
1512
overt act of relinquishment in perpetuity of all present and future rights to
1513
this software under copyright law.
1514
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1515
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1516
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1517
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1518
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1519
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1520
------------------------------------------------------------------------------
1521
*/
/Users/kiltum/projects/zxcpp/lib/imgui/imstb_truetype.h
Line
Count
Source
1
// [DEAR IMGUI]
2
// This is a slightly modified version of stb_truetype.h 1.26.
3
// Mostly fixing for compiler and static analyzer warnings.
4
// Grep for [DEAR IMGUI] to find the changes.
5
6
// stb_truetype.h - v1.26 - public domain
7
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
8
//
9
// =======================================================================
10
//
11
//    NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
12
//
13
// This library does no range checking of the offsets found in the file,
14
// meaning an attacker can use it to read arbitrary memory.
15
//
16
// =======================================================================
17
//
18
//   This library processes TrueType files:
19
//        parse files
20
//        extract glyph metrics
21
//        extract glyph shapes
22
//        render glyphs to one-channel bitmaps with antialiasing (box filter)
23
//        render glyphs to one-channel SDF bitmaps (signed-distance field/function)
24
//
25
//   Todo:
26
//        non-MS cmaps
27
//        crashproof on bad data
28
//        hinting? (no longer patented)
29
//        cleartype-style AA?
30
//        optimize: use simple memory allocator for intermediates
31
//        optimize: build edge-list directly from curves
32
//        optimize: rasterize directly from curves?
33
//
34
// ADDITIONAL CONTRIBUTORS
35
//
36
//   Mikko Mononen: compound shape support, more cmap formats
37
//   Tor Andersson: kerning, subpixel rendering
38
//   Dougall Johnson: OpenType / Type 2 font handling
39
//   Daniel Ribeiro Maciel: basic GPOS-based kerning
40
//
41
//   Misc other:
42
//       Ryan Gordon
43
//       Simon Glass
44
//       github:IntellectualKitty
45
//       Imanol Celaya
46
//       Daniel Ribeiro Maciel
47
//
48
//   Bug/warning reports/fixes:
49
//       "Zer" on mollyrocket       Fabian "ryg" Giesen   github:NiLuJe
50
//       Cass Everitt               Martins Mozeiko       github:aloucks
51
//       stoiko (Haemimont Games)   Cap Petschulat        github:oyvindjam
52
//       Brian Hook                 Omar Cornut           github:vassvik
53
//       Walter van Niftrik         Ryan Griege
54
//       David Gow                  Peter LaValle
55
//       David Given                Sergey Popov
56
//       Ivan-Assen Ivanov          Giumo X. Clanjor
57
//       Anthony Pesch              Higor Euripedes
58
//       Johan Duparc               Thomas Fields
59
//       Hou Qiming                 Derek Vinyard
60
//       Rob Loach                  Cort Stratton
61
//       Kenney Phillis Jr.         Brian Costabile
62
//       Ken Voskuil (kaesve)
63
//
64
// VERSION HISTORY
65
//
66
//   1.26 (2021-08-28) fix broken rasterizer
67
//   1.25 (2021-07-11) many fixes
68
//   1.24 (2020-02-05) fix warning
69
//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
70
//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
71
//   1.21 (2019-02-25) fix warning
72
//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
73
//   1.19 (2018-02-11) GPOS kerning, STBTT_fmod
74
//   1.18 (2018-01-29) add missing function
75
//   1.17 (2017-07-23) make more arguments const; doc fix
76
//   1.16 (2017-07-12) SDF support
77
//   1.15 (2017-03-03) make more arguments const
78
//   1.14 (2017-01-16) num-fonts-in-TTC function
79
//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
80
//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
81
//   1.11 (2016-04-02) fix unused-variable warning
82
//   1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
83
//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
84
//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
85
//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
86
//                     variant PackFontRanges to pack and render in separate phases;
87
//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
88
//                     fixed an assert() bug in the new rasterizer
89
//                     replace assert() with STBTT_assert() in new rasterizer
90
//
91
//   Full history can be found at the end of this file.
92
//
93
// LICENSE
94
//
95
//   See end of file for license information.
96
//
97
// USAGE
98
//
99
//   Include this file in whatever places need to refer to it. In ONE C/C++
100
//   file, write:
101
//      #define STB_TRUETYPE_IMPLEMENTATION
102
//   before the #include of this file. This expands out the actual
103
//   implementation into that C/C++ file.
104
//
105
//   To make the implementation private to the file that generates the implementation,
106
//      #define STBTT_STATIC
107
//
108
//   Simple 3D API (don't ship this, but it's fine for tools and quick start)
109
//           stbtt_BakeFontBitmap()               -- bake a font to a bitmap for use as texture
110
//           stbtt_GetBakedQuad()                 -- compute quad to draw for a given char
111
//
112
//   Improved 3D API (more shippable):
113
//           #include "stb_rect_pack.h"           -- optional, but you really want it
114
//           stbtt_PackBegin()
115
//           stbtt_PackSetOversampling()          -- for improved quality on small fonts
116
//           stbtt_PackFontRanges()               -- pack and renders
117
//           stbtt_PackEnd()
118
//           stbtt_GetPackedQuad()
119
//
120
//   "Load" a font file from a memory buffer (you have to keep the buffer loaded)
121
//           stbtt_InitFont()
122
//           stbtt_GetFontOffsetForIndex()        -- indexing for TTC font collections
123
//           stbtt_GetNumberOfFonts()             -- number of fonts for TTC font collections
124
//
125
//   Render a unicode codepoint to a bitmap
126
//           stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap
127
//           stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide
128
//           stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be
129
//
130
//   Character advance/positioning
131
//           stbtt_GetCodepointHMetrics()
132
//           stbtt_GetFontVMetrics()
133
//           stbtt_GetFontVMetricsOS2()
134
//           stbtt_GetCodepointKernAdvance()
135
//
136
//   Starting with version 1.06, the rasterizer was replaced with a new,
137
//   faster and generally-more-precise rasterizer. The new rasterizer more
138
//   accurately measures pixel coverage for anti-aliasing, except in the case
139
//   where multiple shapes overlap, in which case it overestimates the AA pixel
140
//   coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
141
//   this turns out to be a problem, you can re-enable the old rasterizer with
142
//        #define STBTT_RASTERIZER_VERSION 1
143
//   which will incur about a 15% speed hit.
144
//
145
// ADDITIONAL DOCUMENTATION
146
//
147
//   Immediately after this block comment are a series of sample programs.
148
//
149
//   After the sample programs is the "header file" section. This section
150
//   includes documentation for each API function.
151
//
152
//   Some important concepts to understand to use this library:
153
//
154
//      Codepoint
155
//         Characters are defined by unicode codepoints, e.g. 65 is
156
//         uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
157
//         the hiragana for "ma".
158
//
159
//      Glyph
160
//         A visual character shape (every codepoint is rendered as
161
//         some glyph)
162
//
163
//      Glyph index
164
//         A font-specific integer ID representing a glyph
165
//
166
//      Baseline
167
//         Glyph shapes are defined relative to a baseline, which is the
168
//         bottom of uppercase characters. Characters extend both above
169
//         and below the baseline.
170
//
171
//      Current Point
172
//         As you draw text to the screen, you keep track of a "current point"
173
//         which is the origin of each character. The current point's vertical
174
//         position is the baseline. Even "baked fonts" use this model.
175
//
176
//      Vertical Font Metrics
177
//         The vertical qualities of the font, used to vertically position
178
//         and space the characters. See docs for stbtt_GetFontVMetrics.
179
//
180
//      Font Size in Pixels or Points
181
//         The preferred interface for specifying font sizes in stb_truetype
182
//         is to specify how tall the font's vertical extent should be in pixels.
183
//         If that sounds good enough, skip the next paragraph.
184
//
185
//         Most font APIs instead use "points", which are a common typographic
186
//         measurement for describing font size, defined as 72 points per inch.
187
//         stb_truetype provides a point API for compatibility. However, true
188
//         "per inch" conventions don't make much sense on computer displays
189
//         since different monitors have different number of pixels per
190
//         inch. For example, Windows traditionally uses a convention that
191
//         there are 96 pixels per inch, thus making 'inch' measurements have
192
//         nothing to do with inches, and thus effectively defining a point to
193
//         be 1.333 pixels. Additionally, the TrueType font data provides
194
//         an explicit scale factor to scale a given font's glyphs to points,
195
//         but the author has observed that this scale factor is often wrong
196
//         for non-commercial fonts, thus making fonts scaled in points
197
//         according to the TrueType spec incoherently sized in practice.
198
//
199
// DETAILED USAGE:
200
//
201
//  Scale:
202
//    Select how high you want the font to be, in points or pixels.
203
//    Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute
204
//    a scale factor SF that will be used by all other functions.
205
//
206
//  Baseline:
207
//    You need to select a y-coordinate that is the baseline of where
208
//    your text will appear. Call GetFontBoundingBox to get the baseline-relative
209
//    bounding box for all characters. SF*-y0 will be the distance in pixels
210
//    that the worst-case character could extend above the baseline, so if
211
//    you want the top edge of characters to appear at the top of the
212
//    screen where y=0, then you would set the baseline to SF*-y0.
213
//
214
//  Current point:
215
//    Set the current point where the first character will appear. The
216
//    first character could extend left of the current point; this is font
217
//    dependent. You can either choose a current point that is the leftmost
218
//    point and hope, or add some padding, or check the bounding box or
219
//    left-side-bearing of the first character to be displayed and set
220
//    the current point based on that.
221
//
222
//  Displaying a character:
223
//    Compute the bounding box of the character. It will contain signed values
224
//    relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
225
//    then the character should be displayed in the rectangle from
226
//    <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
227
//
228
//  Advancing for the next character:
229
//    Call GlyphHMetrics, and compute 'current_point += SF * advance'.
230
//
231
//
232
// ADVANCED USAGE
233
//
234
//   Quality:
235
//
236
//    - Use the functions with Subpixel at the end to allow your characters
237
//      to have subpixel positioning. Since the font is anti-aliased, not
238
//      hinted, this is very import for quality. (This is not possible with
239
//      baked fonts.)
240
//
241
//    - Kerning is now supported, and if you're supporting subpixel rendering
242
//      then kerning is worth using to give your text a polished look.
243
//
244
//   Performance:
245
//
246
//    - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
247
//      if you don't do this, stb_truetype is forced to do the conversion on
248
//      every call.
249
//
250
//    - There are a lot of memory allocations. We should modify it to take
251
//      a temp buffer and allocate from the temp buffer (without freeing),
252
//      should help performance a lot.
253
//
254
// NOTES
255
//
256
//   The system uses the raw data found in the .ttf file without changing it
257
//   and without building auxiliary data structures. This is a bit inefficient
258
//   on little-endian systems (the data is big-endian), but assuming you're
259
//   caching the bitmaps or glyph shapes this shouldn't be a big deal.
260
//
261
//   It appears to be very hard to programmatically determine what font a
262
//   given file is in a general way. I provide an API for this, but I don't
263
//   recommend it.
264
//
265
//
266
// PERFORMANCE MEASUREMENTS FOR 1.06:
267
//
268
//                      32-bit     64-bit
269
//   Previous release:  8.83 s     7.68 s
270
//   Pool allocations:  7.72 s     6.34 s
271
//   Inline sort     :  6.54 s     5.65 s
272
//   New rasterizer  :  5.63 s     5.00 s
273
274
//////////////////////////////////////////////////////////////////////////////
275
//////////////////////////////////////////////////////////////////////////////
276
////
277
////  SAMPLE PROGRAMS
278
////
279
//
280
//  Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
281
//  See "tests/truetype_demo_win32.c" for a complete version.
282
#if 0
283
#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation
284
#include "stb_truetype.h"
285
286
unsigned char ttf_buffer[1<<20];
287
unsigned char temp_bitmap[512*512];
288
289
stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
290
GLuint ftex;
291
292
void my_stbtt_initfont(void)
293
{
294
   fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
295
   stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
296
   // can free ttf_buffer at this point
297
   glGenTextures(1, &ftex);
298
   glBindTexture(GL_TEXTURE_2D, ftex);
299
   glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
300
   // can free temp_bitmap at this point
301
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
302
}
303
304
void my_stbtt_print(float x, float y, char *text)
305
{
306
   // assume orthographic projection with units = screen pixels, origin at top left
307
   glEnable(GL_BLEND);
308
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
309
   glEnable(GL_TEXTURE_2D);
310
   glBindTexture(GL_TEXTURE_2D, ftex);
311
   glBegin(GL_QUADS);
312
   while (*text) {
313
      if (*text >= 32 && *text < 128) {
314
         stbtt_aligned_quad q;
315
         stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
316
         glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
317
         glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
318
         glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
319
         glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
320
      }
321
      ++text;
322
   }
323
   glEnd();
324
}
325
#endif
326
//
327
//
328
//////////////////////////////////////////////////////////////////////////////
329
//
330
// Complete program (this compiles): get a single bitmap, print as ASCII art
331
//
332
#if 0
333
#include <stdio.h>
334
#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation
335
#include "stb_truetype.h"
336
337
char ttf_buffer[1<<25];
338
339
int main(int argc, char **argv)
340
{
341
   stbtt_fontinfo font;
342
   unsigned char *bitmap;
343
   int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
344
345
   fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
346
347
   stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
348
   bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
349
350
   for (j=0; j < h; ++j) {
351
      for (i=0; i < w; ++i)
352
         putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
353
      putchar('\n');
354
   }
355
   return 0;
356
}
357
#endif
358
//
359
// Output:
360
//
361
//     .ii.
362
//    @@@@@@.
363
//   V@Mio@@o
364
//   :i.  V@V
365
//     :oM@@M
366
//   :@@@MM@M
367
//   @@o  o@M
368
//  :@@.  M@M
369
//   @@@o@@@@
370
//   :M@@V:@@.
371
//
372
//////////////////////////////////////////////////////////////////////////////
373
//
374
// Complete program: print "Hello World!" banner, with bugs
375
//
376
#if 0
377
char buffer[24<<20];
378
unsigned char screen[20][79];
379
380
int main(int arg, char **argv)
381
{
382
   stbtt_fontinfo font;
383
   int i,j,ascent,baseline,ch=0;
384
   float scale, xpos=2; // leave a little padding in case the character extends left
385
   char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
386
387
   fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
388
   stbtt_InitFont(&font, buffer, 0);
389
390
   scale = stbtt_ScaleForPixelHeight(&font, 15);
391
   stbtt_GetFontVMetrics(&font, &ascent,0,0);
392
   baseline = (int) (ascent*scale);
393
394
   while (text[ch]) {
395
      int advance,lsb,x0,y0,x1,y1;
396
      float x_shift = xpos - (float) floor(xpos);
397
      stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
398
      stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
399
      stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
400
      // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
401
      // because this API is really for baking character bitmaps into textures. if you want to render
402
      // a sequence of characters, you really need to render each bitmap to a temp buffer, then
403
      // "alpha blend" that into the working buffer
404
      xpos += (advance * scale);
405
      if (text[ch+1])
406
         xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
407
      ++ch;
408
   }
409
410
   for (j=0; j < 20; ++j) {
411
      for (i=0; i < 78; ++i)
412
         putchar(" .:ioVM@"[screen[j][i]>>5]);
413
      putchar('\n');
414
   }
415
416
   return 0;
417
}
418
#endif
419
420
421
//////////////////////////////////////////////////////////////////////////////
422
//////////////////////////////////////////////////////////////////////////////
423
////
424
////   INTEGRATION WITH YOUR CODEBASE
425
////
426
////   The following sections allow you to supply alternate definitions
427
////   of C library functions used by stb_truetype, e.g. if you don't
428
////   link with the C runtime library.
429
430
#ifdef STB_TRUETYPE_IMPLEMENTATION
431
   // #define your own (u)stbtt_int8/16/32 before including to override this
432
   #ifndef stbtt_uint8
433
   typedef unsigned char   stbtt_uint8;
434
   typedef signed   char   stbtt_int8;
435
   typedef unsigned short  stbtt_uint16;
436
   typedef signed   short  stbtt_int16;
437
   typedef unsigned int    stbtt_uint32;
438
   typedef signed   int    stbtt_int32;
439
   #endif
440
441
   typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
442
   typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
443
444
   // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
445
   #ifndef STBTT_ifloor
446
   #include <math.h>
447
   #define STBTT_ifloor(x)   ((int) floor(x))
448
   #define STBTT_iceil(x)    ((int) ceil(x))
449
   #endif
450
451
   #ifndef STBTT_sqrt
452
   #include <math.h>
453
   #define STBTT_sqrt(x)      sqrt(x)
454
   #define STBTT_pow(x,y)     pow(x,y)
455
   #endif
456
457
   #ifndef STBTT_fmod
458
   #include <math.h>
459
   #define STBTT_fmod(x,y)    fmod(x,y)
460
   #endif
461
462
   #ifndef STBTT_cos
463
   #include <math.h>
464
   #define STBTT_cos(x)       cos(x)
465
   #define STBTT_acos(x)      acos(x)
466
   #endif
467
468
   #ifndef STBTT_fabs
469
   #include <math.h>
470
   #define STBTT_fabs(x)      fabs(x)
471
   #endif
472
473
   // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
474
   #ifndef STBTT_malloc
475
   #include <stdlib.h>
476
   #define STBTT_malloc(x,u)  ((void)(u),malloc(x))
477
   #define STBTT_free(x,u)    ((void)(u),free(x))
478
   #endif
479
480
   #ifndef STBTT_assert
481
   #include <assert.h>
482
   #define STBTT_assert(x)    assert(x)
483
   #endif
484
485
   #ifndef STBTT_strlen
486
   #include <string.h>
487
   #define STBTT_strlen(x)    strlen(x)
488
   #endif
489
490
   #ifndef STBTT_memcpy
491
   #include <string.h>
492
0
   #define STBTT_memcpy       memcpy
493
306
   #define STBTT_memset       memset
494
   #endif
495
#endif
496
497
///////////////////////////////////////////////////////////////////////////////
498
///////////////////////////////////////////////////////////////////////////////
499
////
500
////   INTERFACE
501
////
502
////
503
504
#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
505
#define __STB_INCLUDE_STB_TRUETYPE_H__
506
507
#ifdef STBTT_STATIC
508
#define STBTT_DEF static
509
#else
510
#define STBTT_DEF extern
511
#endif
512
513
#ifdef __cplusplus
514
extern "C" {
515
#endif
516
517
// private structure
518
typedef struct
519
{
520
   unsigned char *data;
521
   int cursor;
522
   int size;
523
} stbtt__buf;
524
525
//////////////////////////////////////////////////////////////////////////////
526
//
527
// TEXTURE BAKING API
528
//
529
// If you use this API, you only have to call two functions ever.
530
//
531
532
typedef struct
533
{
534
   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
535
   float xoff,yoff,xadvance;
536
} stbtt_bakedchar;
537
538
STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)
539
                                float pixel_height,                     // height of font in pixels
540
                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in
541
                                int first_char, int num_chars,          // characters to bake
542
                                stbtt_bakedchar *chardata);             // you allocate this, it's num_chars long
543
// if return is positive, the first unused row of the bitmap
544
// if return is negative, returns the negative of the number of characters that fit
545
// if return is 0, no characters fit and no rows were used
546
// This uses a very crappy packing.
547
548
typedef struct
549
{
550
   float x0,y0,s0,t0; // top-left
551
   float x1,y1,s1,t1; // bottom-right
552
} stbtt_aligned_quad;
553
554
STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph,  // same data as above
555
                               int char_index,             // character to display
556
                               float *xpos, float *ypos,   // pointers to current position in screen pixel space
557
                               stbtt_aligned_quad *q,      // output: quad to draw
558
                               int opengl_fillrule);       // true if opengl fill rule; false if DX9 or earlier
559
// Call GetBakedQuad with char_index = 'character - first_char', and it
560
// creates the quad you need to draw and advances the current position.
561
//
562
// The coordinate system used assumes y increases downwards.
563
//
564
// Characters will extend both above and below the current position;
565
// see discussion of "BASELINE" above.
566
//
567
// It's inefficient; you might want to c&p it and optimize it.
568
569
STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);
570
// Query the font vertical metrics without having to create a font first.
571
572
573
//////////////////////////////////////////////////////////////////////////////
574
//
575
// NEW TEXTURE BAKING API
576
//
577
// This provides options for packing multiple fonts into one atlas, not
578
// perfectly but better than nothing.
579
580
typedef struct
581
{
582
   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
583
   float xoff,yoff,xadvance;
584
   float xoff2,yoff2;
585
} stbtt_packedchar;
586
587
typedef struct stbtt_pack_context stbtt_pack_context;
588
typedef struct stbtt_fontinfo stbtt_fontinfo;
589
#ifndef STB_RECT_PACK_VERSION
590
typedef struct stbrp_rect stbrp_rect;
591
#endif
592
593
STBTT_DEF int  stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
594
// Initializes a packing context stored in the passed-in stbtt_pack_context.
595
// Future calls using this context will pack characters into the bitmap passed
596
// in here: a 1-channel bitmap that is width * height. stride_in_bytes is
597
// the distance from one row to the next (or 0 to mean they are packed tightly
598
// together). "padding" is the amount of padding to leave between each
599
// character (normally you want '1' for bitmaps you'll use as textures with
600
// bilinear filtering).
601
//
602
// Returns 0 on failure, 1 on success.
603
604
STBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc);
605
// Cleans up the packing context and frees all memory.
606
607
#define STBTT_POINT_SIZE(x)   (-(x))
608
609
STBTT_DEF int  stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
610
                                int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
611
// Creates character bitmaps from the font_index'th font found in fontdata (use
612
// font_index=0 if you don't know what that is). It creates num_chars_in_range
613
// bitmaps for characters with unicode values starting at first_unicode_char_in_range
614
// and increasing. Data for how to render them is stored in chardata_for_range;
615
// pass these to stbtt_GetPackedQuad to get back renderable quads.
616
//
617
// font_size is the full height of the character from ascender to descender,
618
// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
619
// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
620
// and pass that result as 'font_size':
621
//       ...,                  20 , ... // font max minus min y is 20 pixels tall
622
//       ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
623
624
typedef struct
625
{
626
   float font_size;
627
   int first_unicode_codepoint_in_range;  // if non-zero, then the chars are continuous, and this is the first codepoint
628
   int *array_of_unicode_codepoints;       // if non-zero, then this is an array of unicode codepoints
629
   int num_chars;
630
   stbtt_packedchar *chardata_for_range; // output
631
   unsigned char h_oversample, v_oversample; // don't set these, they're used internally
632
} stbtt_pack_range;
633
634
STBTT_DEF int  stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
635
// Creates character bitmaps from multiple ranges of characters stored in
636
// ranges. This will usually create a better-packed bitmap than multiple
637
// calls to stbtt_PackFontRange. Note that you can call this multiple
638
// times within a single PackBegin/PackEnd.
639
640
STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
641
// Oversampling a font increases the quality by allowing higher-quality subpixel
642
// positioning, and is especially valuable at smaller text sizes.
643
//
644
// This function sets the amount of oversampling for all following calls to
645
// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
646
// pack context. The default (no oversampling) is achieved by h_oversample=1
647
// and v_oversample=1. The total number of pixels required is
648
// h_oversample*v_oversample larger than the default; for example, 2x2
649
// oversampling requires 4x the storage of 1x1. For best results, render
650
// oversampled textures with bilinear filtering. Look at the readme in
651
// stb/tests/oversample for information about oversampled fonts
652
//
653
// To use with PackFontRangesGather etc., you must set it before calls
654
// call to PackFontRangesGatherRects.
655
656
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
657
// If skip != 0, this tells stb_truetype to skip any codepoints for which
658
// there is no corresponding glyph. If skip=0, which is the default, then
659
// codepoints without a glyph received the font's "missing character" glyph,
660
// typically an empty box by convention.
661
662
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph,  // same data as above
663
                               int char_index,             // character to display
664
                               float *xpos, float *ypos,   // pointers to current position in screen pixel space
665
                               stbtt_aligned_quad *q,      // output: quad to draw
666
                               int align_to_integer);
667
668
STBTT_DEF int  stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
669
STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
670
STBTT_DEF int  stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
671
// Calling these functions in sequence is roughly equivalent to calling
672
// stbtt_PackFontRanges(). If you more control over the packing of multiple
673
// fonts, or if you want to pack custom data into a font texture, take a look
674
// at the source to of stbtt_PackFontRanges() and create a custom version
675
// using these functions, e.g. call GatherRects multiple times,
676
// building up a single array of rects, then call PackRects once,
677
// then call RenderIntoRects repeatedly. This may result in a
678
// better packing than calling PackFontRanges multiple times
679
// (or it may not).
680
681
// this is an opaque structure that you shouldn't mess with which holds
682
// all the context needed from PackBegin to PackEnd.
683
struct stbtt_pack_context {
684
   void *user_allocator_context;
685
   void *pack_info;
686
   int   width;
687
   int   height;
688
   int   stride_in_bytes;
689
   int   padding;
690
   int   skip_missing;
691
   unsigned int   h_oversample, v_oversample;
692
   unsigned char *pixels;
693
   void  *nodes;
694
};
695
696
//////////////////////////////////////////////////////////////////////////////
697
//
698
// FONT LOADING
699
//
700
//
701
702
STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);
703
// This function will determine the number of fonts in a font file.  TrueType
704
// collection (.ttc) files may contain multiple fonts, while TrueType font
705
// (.ttf) files only contain one font. The number of fonts can be used for
706
// indexing with the previous function where the index is between zero and one
707
// less than the total fonts. If an error occurs, -1 is returned.
708
709
STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
710
// Each .ttf/.ttc file may have more than one font. Each font has a sequential
711
// index number starting from 0. Call this function to get the font offset for
712
// a given index; it returns -1 if the index is out of range. A regular .ttf
713
// file will only define one font and it always be at offset 0, so it will
714
// return '0' for index 0, and -1 for all other indices.
715
716
// The following structure is defined publicly so you can declare one on
717
// the stack or as a global or etc, but you should treat it as opaque.
718
struct stbtt_fontinfo
719
{
720
   void           * userdata;
721
   unsigned char  * data;              // pointer to .ttf file
722
   int              fontstart;         // offset of start of font
723
724
   int numGlyphs;                     // number of glyphs, needed for range checking
725
726
   int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf
727
   int index_map;                     // a cmap mapping for our chosen character encoding
728
   int indexToLocFormat;              // format needed to map from glyph index to glyph
729
730
   stbtt__buf cff;                    // cff font data
731
   stbtt__buf charstrings;            // the charstring index
732
   stbtt__buf gsubrs;                 // global charstring subroutines index
733
   stbtt__buf subrs;                  // private charstring subroutines index
734
   stbtt__buf fontdicts;              // array of font dicts
735
   stbtt__buf fdselect;               // map from glyph to fontdict
736
};
737
738
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
739
// Given an offset into the file that defines a font, this function builds
740
// the necessary cached info for the rest of the system. You must allocate
741
// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
742
// need to do anything special to free it, because the contents are pure
743
// value data with no additional data structures. Returns 0 on failure.
744
745
746
//////////////////////////////////////////////////////////////////////////////
747
//
748
// CHARACTER TO GLYPH-INDEX CONVERSIOn
749
750
STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
751
// If you're going to perform multiple operations on the same character
752
// and you want a speed-up, call this function with the character you're
753
// going to process, then use glyph-based functions instead of the
754
// codepoint-based functions.
755
// Returns 0 if the character codepoint is not defined in the font.
756
757
758
//////////////////////////////////////////////////////////////////////////////
759
//
760
// CHARACTER PROPERTIES
761
//
762
763
STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
764
// computes a scale factor to produce a font whose "height" is 'pixels' tall.
765
// Height is measured as the distance from the highest ascender to the lowest
766
// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
767
// and computing:
768
//       scale = pixels / (ascent - descent)
769
// so if you prefer to measure height by the ascent only, use a similar calculation.
770
771
STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
772
// computes a scale factor to produce a font whose EM size is mapped to
773
// 'pixels' tall. This is probably what traditional APIs compute, but
774
// I'm not positive.
775
776
STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
777
// ascent is the coordinate above the baseline the font extends; descent
778
// is the coordinate below the baseline the font extends (i.e. it is typically negative)
779
// lineGap is the spacing between one row's descent and the next row's ascent...
780
// so you should advance the vertical position by "*ascent - *descent + *lineGap"
781
//   these are expressed in unscaled coordinates, so you must multiply by
782
//   the scale factor for a given size
783
784
STBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);
785
// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2
786
// table (specific to MS/Windows TTF files).
787
//
788
// Returns 1 on success (table present), 0 on failure.
789
790
STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
791
// the bounding box around all possible characters
792
793
STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
794
// leftSideBearing is the offset from the current horizontal position to the left edge of the character
795
// advanceWidth is the offset from the current horizontal position to the next horizontal position
796
//   these are expressed in unscaled coordinates
797
798
STBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
799
// an additional amount to add to the 'advance' value between ch1 and ch2
800
801
STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
802
// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
803
804
STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
805
STBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
806
STBTT_DEF int  stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
807
// as above, but takes one or more glyph indices for greater efficiency
808
809
typedef struct stbtt_kerningentry
810
{
811
   int glyph1; // use stbtt_FindGlyphIndex
812
   int glyph2;
813
   int advance;
814
} stbtt_kerningentry;
815
816
STBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info);
817
STBTT_DEF int  stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);
818
// Retrieves a complete list of all of the kerning pairs provided by the font
819
// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.
820
// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)
821
822
//////////////////////////////////////////////////////////////////////////////
823
//
824
// GLYPH SHAPES (you probably don't need these, but they have to go before
825
// the bitmaps for C declaration-order reasons)
826
//
827
828
#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
829
   enum {
830
      STBTT_vmove=1,
831
      STBTT_vline,
832
      STBTT_vcurve,
833
      STBTT_vcubic
834
   };
835
#endif
836
837
#ifndef stbtt_vertex // you can predefine this to use different values
838
                   // (we share this with other code at RAD)
839
0
   #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
840
   typedef struct
841
   {
842
      stbtt_vertex_type x,y,cx,cy,cx1,cy1;
843
      unsigned char type,padding;
844
   } stbtt_vertex;
845
#endif
846
847
STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
848
// returns non-zero if nothing is drawn for this glyph
849
850
STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
851
STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
852
// returns # of vertices and fills *vertices with the pointer to them
853
//   these are expressed in "unscaled" coordinates
854
//
855
// The shape is a series of contours. Each one starts with
856
// a STBTT_moveto, then consists of a series of mixed
857
// STBTT_lineto and STBTT_curveto segments. A lineto
858
// draws a line from previous endpoint to its x,y; a curveto
859
// draws a quadratic bezier from previous endpoint to
860
// its x,y, using cx,cy as the bezier control point.
861
862
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
863
// frees the data allocated above
864
865
STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);
866
STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);
867
STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
868
// fills svg with the character's SVG data.
869
// returns data size or 0 if SVG not found.
870
871
//////////////////////////////////////////////////////////////////////////////
872
//
873
// BITMAP RENDERING
874
//
875
876
STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
877
// frees the bitmap allocated below
878
879
STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
880
// allocates a large-enough single-channel 8bpp bitmap and renders the
881
// specified character/glyph at the specified scale into it, with
882
// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
883
// *width & *height are filled out with the width & height of the bitmap,
884
// which is stored left-to-right, top-to-bottom.
885
//
886
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
887
888
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
889
// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
890
// shift for the character
891
892
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
893
// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
894
// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
895
// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
896
// width and height and positioning info for it first.
897
898
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
899
// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
900
// shift for the character
901
902
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);
903
// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
904
// is performed (see stbtt_PackSetOversampling)
905
906
STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
907
// get the bbox of the bitmap centered around the glyph origin; so the
908
// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
909
// the bitmap top left is (leftSideBearing*scale,iy0).
910
// (Note that the bitmap uses y-increases-down, but the shape uses
911
// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
912
913
STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
914
// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
915
// shift for the character
916
917
// the following functions are equivalent to the above functions, but operate
918
// on glyph indices instead of Unicode codepoints (for efficiency)
919
STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
920
STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
921
STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
922
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
923
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);
924
STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
925
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
926
927
928
// @TODO: don't expose this structure
929
typedef struct
930
{
931
   int w,h,stride;
932
   unsigned char *pixels;
933
} stbtt__bitmap;
934
935
// rasterize a shape with quadratic beziers into a bitmap
936
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result,        // 1-channel bitmap to draw into
937
                               float flatness_in_pixels,     // allowable error of curve in pixels
938
                               stbtt_vertex *vertices,       // array of vertices defining shape
939
                               int num_verts,                // number of vertices in above array
940
                               float scale_x, float scale_y, // scale applied to input vertices
941
                               float shift_x, float shift_y, // translation applied to input vertices
942
                               int x_off, int y_off,         // another translation applied to input
943
                               int invert,                   // if non-zero, vertically flip shape
944
                               void *userdata);              // context for to STBTT_MALLOC
945
946
//////////////////////////////////////////////////////////////////////////////
947
//
948
// Signed Distance Function (or Field) rendering
949
950
STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);
951
// frees the SDF bitmap allocated below
952
953
STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
954
STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
955
// These functions compute a discretized SDF field for a single character, suitable for storing
956
// in a single-channel texture, sampling with bilinear filtering, and testing against
957
// larger than some threshold to produce scalable fonts.
958
//        info              --  the font
959
//        scale             --  controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
960
//        glyph/codepoint   --  the character to generate the SDF for
961
//        padding           --  extra "pixels" around the character which are filled with the distance to the character (not 0),
962
//                                 which allows effects like bit outlines
963
//        onedge_value      --  value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
964
//        pixel_dist_scale  --  what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
965
//                                 if positive, > onedge_value is inside; if negative, < onedge_value is inside
966
//        width,height      --  output height & width of the SDF bitmap (including padding)
967
//        xoff,yoff         --  output origin of the character
968
//        return value      --  a 2D array of bytes 0..255, width*height in size
969
//
970
// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
971
// optimal use of the limited 0..255 for your application, trading off precision
972
// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
973
//
974
// Example:
975
//      scale = stbtt_ScaleForPixelHeight(22)
976
//      padding = 5
977
//      onedge_value = 180
978
//      pixel_dist_scale = 180/5.0 = 36.0
979
//
980
//      This will create an SDF bitmap in which the character is about 22 pixels
981
//      high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
982
//      shape, sample the SDF at each pixel and fill the pixel if the SDF value
983
//      is greater than or equal to 180/255. (You'll actually want to antialias,
984
//      which is beyond the scope of this example.) Additionally, you can compute
985
//      offset outlines (e.g. to stroke the character border inside & outside,
986
//      or only outside). For example, to fill outside the character up to 3 SDF
987
//      pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
988
//      choice of variables maps a range from 5 pixels outside the shape to
989
//      2 pixels inside the shape to 0..255; this is intended primarily for apply
990
//      outside effects only (the interior range is needed to allow proper
991
//      antialiasing of the font at *smaller* sizes)
992
//
993
// The function computes the SDF analytically at each SDF pixel, not by e.g.
994
// building a higher-res bitmap and approximating it. In theory the quality
995
// should be as high as possible for an SDF of this size & representation, but
996
// unclear if this is true in practice (perhaps building a higher-res bitmap
997
// and computing from that can allow drop-out prevention).
998
//
999
// The algorithm has not been optimized at all, so expect it to be slow
1000
// if computing lots of characters or very large sizes.
1001
1002
1003
1004
//////////////////////////////////////////////////////////////////////////////
1005
//
1006
// Finding the right font...
1007
//
1008
// You should really just solve this offline, keep your own tables
1009
// of what font is what, and don't try to get it out of the .ttf file.
1010
// That's because getting it out of the .ttf file is really hard, because
1011
// the names in the file can appear in many possible encodings, in many
1012
// possible languages, and e.g. if you need a case-insensitive comparison,
1013
// the details of that depend on the encoding & language in a complex way
1014
// (actually underspecified in truetype, but also gigantic).
1015
//
1016
// But you can use the provided functions in two possible ways:
1017
//     stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
1018
//             unicode-encoded names to try to find the font you want;
1019
//             you can run this before calling stbtt_InitFont()
1020
//
1021
//     stbtt_GetFontNameString() lets you get any of the various strings
1022
//             from the file yourself and do your own comparisons on them.
1023
//             You have to have called stbtt_InitFont() first.
1024
1025
1026
STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
1027
// returns the offset (not index) of the font that matches, or -1 if none
1028
//   if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
1029
//   if you use any other flag, use a font name like "Arial"; this checks
1030
//     the 'macStyle' header field; i don't know if fonts set this consistently
1031
#define STBTT_MACSTYLE_DONTCARE     0
1032
#define STBTT_MACSTYLE_BOLD         1
1033
#define STBTT_MACSTYLE_ITALIC       2
1034
#define STBTT_MACSTYLE_UNDERSCORE   4
1035
#define STBTT_MACSTYLE_NONE         8   // <= not same as 0, this makes us check the bitfield is 0
1036
1037
STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
1038
// returns 1/0 whether the first string interpreted as utf8 is identical to
1039
// the second string interpreted as big-endian utf16... useful for strings from next func
1040
1041
STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
1042
// returns the string (which may be big-endian double byte, e.g. for unicode)
1043
// and puts the length in bytes in *length.
1044
//
1045
// some of the values for the IDs are below; for more see the truetype spec:
1046
//     http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
1047
//     http://www.microsoft.com/typography/otspec/name.htm
1048
1049
enum { // platformID
1050
   STBTT_PLATFORM_ID_UNICODE   =0,
1051
   STBTT_PLATFORM_ID_MAC       =1,
1052
   STBTT_PLATFORM_ID_ISO       =2,
1053
   STBTT_PLATFORM_ID_MICROSOFT =3
1054
};
1055
1056
enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
1057
   STBTT_UNICODE_EID_UNICODE_1_0    =0,
1058
   STBTT_UNICODE_EID_UNICODE_1_1    =1,
1059
   STBTT_UNICODE_EID_ISO_10646      =2,
1060
   STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
1061
   STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
1062
};
1063
1064
enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
1065
   STBTT_MS_EID_SYMBOL        =0,
1066
   STBTT_MS_EID_UNICODE_BMP   =1,
1067
   STBTT_MS_EID_SHIFTJIS      =2,
1068
   STBTT_MS_EID_UNICODE_FULL  =10
1069
};
1070
1071
enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
1072
   STBTT_MAC_EID_ROMAN        =0,   STBTT_MAC_EID_ARABIC       =4,
1073
   STBTT_MAC_EID_JAPANESE     =1,   STBTT_MAC_EID_HEBREW       =5,
1074
   STBTT_MAC_EID_CHINESE_TRAD =2,   STBTT_MAC_EID_GREEK        =6,
1075
   STBTT_MAC_EID_KOREAN       =3,   STBTT_MAC_EID_RUSSIAN      =7
1076
};
1077
1078
enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
1079
       // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
1080
   STBTT_MS_LANG_ENGLISH     =0x0409,   STBTT_MS_LANG_ITALIAN     =0x0410,
1081
   STBTT_MS_LANG_CHINESE     =0x0804,   STBTT_MS_LANG_JAPANESE    =0x0411,
1082
   STBTT_MS_LANG_DUTCH       =0x0413,   STBTT_MS_LANG_KOREAN      =0x0412,
1083
   STBTT_MS_LANG_FRENCH      =0x040c,   STBTT_MS_LANG_RUSSIAN     =0x0419,
1084
   STBTT_MS_LANG_GERMAN      =0x0407,   STBTT_MS_LANG_SPANISH     =0x0409,
1085
   STBTT_MS_LANG_HEBREW      =0x040d,   STBTT_MS_LANG_SWEDISH     =0x041D
1086
};
1087
1088
enum { // languageID for STBTT_PLATFORM_ID_MAC
1089
   STBTT_MAC_LANG_ENGLISH      =0 ,   STBTT_MAC_LANG_JAPANESE     =11,
1090
   STBTT_MAC_LANG_ARABIC       =12,   STBTT_MAC_LANG_KOREAN       =23,
1091
   STBTT_MAC_LANG_DUTCH        =4 ,   STBTT_MAC_LANG_RUSSIAN      =32,
1092
   STBTT_MAC_LANG_FRENCH       =1 ,   STBTT_MAC_LANG_SPANISH      =6 ,
1093
   STBTT_MAC_LANG_GERMAN       =2 ,   STBTT_MAC_LANG_SWEDISH      =5 ,
1094
   STBTT_MAC_LANG_HEBREW       =10,   STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
1095
   STBTT_MAC_LANG_ITALIAN      =3 ,   STBTT_MAC_LANG_CHINESE_TRAD =19
1096
};
1097
1098
#ifdef __cplusplus
1099
}
1100
#endif
1101
1102
#endif // __STB_INCLUDE_STB_TRUETYPE_H__
1103
1104
///////////////////////////////////////////////////////////////////////////////
1105
///////////////////////////////////////////////////////////////////////////////
1106
////
1107
////   IMPLEMENTATION
1108
////
1109
////
1110
1111
#ifdef STB_TRUETYPE_IMPLEMENTATION
1112
1113
#ifndef STBTT_MAX_OVERSAMPLE
1114
0
#define STBTT_MAX_OVERSAMPLE   8
1115
#endif
1116
1117
#if STBTT_MAX_OVERSAMPLE > 255
1118
#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
1119
#endif
1120
1121
typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
1122
1123
#ifndef STBTT_RASTERIZER_VERSION
1124
#define STBTT_RASTERIZER_VERSION 2
1125
#endif
1126
1127
#ifdef _MSC_VER
1128
#define STBTT__NOTUSED(v)  (void)(v)
1129
#else
1130
20
#define STBTT__NOTUSED(v)  (void)sizeof(v)
1131
#endif
1132
1133
//////////////////////////////////////////////////////////////////////////
1134
//
1135
// stbtt__buf helpers to parse data from file
1136
//
1137
1138
static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)
1139
0
{
1140
0
   if (b->cursor >= b->size)
1141
0
      return 0;
1142
0
   return b->data[b->cursor++];
1143
0
}
1144
1145
static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)
1146
0
{
1147
0
   if (b->cursor >= b->size)
1148
0
      return 0;
1149
0
   return b->data[b->cursor];
1150
0
}
1151
1152
static void stbtt__buf_seek(stbtt__buf *b, int o)
1153
0
{
1154
0
   STBTT_assert(!(o > b->size || o < 0));
1155
0
   b->cursor = (o > b->size || o < 0) ? b->size : o;
1156
0
}
1157
1158
static void stbtt__buf_skip(stbtt__buf *b, int o)
1159
0
{
1160
0
   stbtt__buf_seek(b, b->cursor + o);
1161
0
}
1162
1163
static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)
1164
0
{
1165
0
   stbtt_uint32 v = 0;
1166
0
   int i;
1167
0
   STBTT_assert(n >= 1 && n <= 4);
1168
0
   for (i = 0; i < n; i++)
1169
0
      v = (v << 8) | stbtt__buf_get8(b);
1170
0
   return v;
1171
0
}
1172
1173
static stbtt__buf stbtt__new_buf(const void *p, size_t size)
1174
1
{
1175
1
   stbtt__buf r;
1176
1
   STBTT_assert(size < 0x40000000);
1177
1
   r.data = (stbtt_uint8*) p;
1178
1
   r.size = (int) size;
1179
1
   r.cursor = 0;
1180
1
   return r;
1181
1
}
1182
1183
0
#define stbtt__buf_get16(b)  stbtt__buf_get((b), 2)
1184
0
#define stbtt__buf_get32(b)  stbtt__buf_get((b), 4)
1185
1186
static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)
1187
0
{
1188
0
   stbtt__buf r = stbtt__new_buf(NULL, 0);
1189
0
   if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
1190
0
   r.data = b->data + o;
1191
0
   r.size = s;
1192
0
   return r;
1193
0
}
1194
1195
static stbtt__buf stbtt__cff_get_index(stbtt__buf *b)
1196
0
{
1197
0
   int count, start, offsize;
1198
0
   start = b->cursor;
1199
0
   count = stbtt__buf_get16(b);
1200
0
   if (count) {
1201
0
      offsize = stbtt__buf_get8(b);
1202
0
      STBTT_assert(offsize >= 1 && offsize <= 4);
1203
0
      stbtt__buf_skip(b, offsize * count);
1204
0
      stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);
1205
0
   }
1206
0
   return stbtt__buf_range(b, start, b->cursor - start);
1207
0
}
1208
1209
static stbtt_uint32 stbtt__cff_int(stbtt__buf *b)
1210
0
{
1211
0
   int b0 = stbtt__buf_get8(b);
1212
0
   if (b0 >= 32 && b0 <= 246)       return b0 - 139;
1213
0
   else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;
1214
0
   else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;
1215
0
   else if (b0 == 28)               return stbtt__buf_get16(b);
1216
0
   else if (b0 == 29)               return stbtt__buf_get32(b);
1217
0
   STBTT_assert(0);
1218
0
   return 0;
1219
0
}
1220
1221
0
static void stbtt__cff_skip_operand(stbtt__buf *b) {
1222
0
   int v, b0 = stbtt__buf_peek8(b);
1223
0
   STBTT_assert(b0 >= 28);
1224
0
   if (b0 == 30) {
1225
0
      stbtt__buf_skip(b, 1);
1226
0
      while (b->cursor < b->size) {
1227
0
         v = stbtt__buf_get8(b);
1228
0
         if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
1229
0
            break;
1230
0
      }
1231
0
   } else {
1232
0
      stbtt__cff_int(b);
1233
0
   }
1234
0
}
1235
1236
static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)
1237
0
{
1238
0
   stbtt__buf_seek(b, 0);
1239
0
   while (b->cursor < b->size) {
1240
0
      int start = b->cursor, end, op;
1241
0
      while (stbtt__buf_peek8(b) >= 28)
1242
0
         stbtt__cff_skip_operand(b);
1243
0
      end = b->cursor;
1244
0
      op = stbtt__buf_get8(b);
1245
0
      if (op == 12)  op = stbtt__buf_get8(b) | 0x100;
1246
0
      if (op == key) return stbtt__buf_range(b, start, end-start);
1247
0
   }
1248
0
   return stbtt__buf_range(b, 0, 0);
1249
0
}
1250
1251
static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)
1252
0
{
1253
0
   int i;
1254
0
   stbtt__buf operands = stbtt__dict_get(b, key);
1255
0
   for (i = 0; i < outcount && operands.cursor < operands.size; i++)
1256
0
      out[i] = stbtt__cff_int(&operands);
1257
0
}
1258
1259
static int stbtt__cff_index_count(stbtt__buf *b)
1260
0
{
1261
0
   stbtt__buf_seek(b, 0);
1262
0
   return stbtt__buf_get16(b);
1263
0
}
1264
1265
static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)
1266
0
{
1267
0
   int count, offsize, start, end;
1268
0
   stbtt__buf_seek(&b, 0);
1269
0
   count = stbtt__buf_get16(&b);
1270
0
   offsize = stbtt__buf_get8(&b);
1271
0
   STBTT_assert(i >= 0 && i < count);
1272
0
   STBTT_assert(offsize >= 1 && offsize <= 4);
1273
0
   stbtt__buf_skip(&b, i*offsize);
1274
0
   start = stbtt__buf_get(&b, offsize);
1275
0
   end = stbtt__buf_get(&b, offsize);
1276
0
   return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
1277
0
}
1278
1279
//////////////////////////////////////////////////////////////////////////
1280
//
1281
// accessors to parse data from file
1282
//
1283
1284
// on platforms that don't allow misaligned reads, if we want to allow
1285
// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
1286
1287
0
#define ttBYTE(p)     (* (stbtt_uint8 *) (p))
1288
0
#define ttCHAR(p)     (* (stbtt_int8 *) (p))
1289
#define ttFixed(p)    ttLONG(p)
1290
1291
835
static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
1292
333
static stbtt_int16 ttSHORT(stbtt_uint8 *p)   { return p[0]*256 + p[1]; }
1293
9
static stbtt_uint32 ttULONG(stbtt_uint8 *p)  { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
1294
0
static stbtt_int32 ttLONG(stbtt_uint8 *p)    { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
1295
1296
69
#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
1297
67
#define stbtt_tag(p,str)           stbtt_tag4(p,str[0],str[1],str[2],str[3])
1298
1299
static int stbtt__isfont(stbtt_uint8 *font)
1300
1
{
1301
   // check the version number
1302
1
   if (stbtt_tag4(font, '1',0,0,0))  return 1; // TrueType 1
1303
1
   if (stbtt_tag(font, "typ1"))   return 1; // TrueType with type 1 font -- we don't support this!
1304
1
   if (stbtt_tag(font, "OTTO"))   return 1; // OpenType with CFF
1305
1
   if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
1306
0
   if (stbtt_tag(font, "true"))   return 1; // Apple specification for TrueType fonts
1307
0
   return 0;
1308
0
}
1309
1310
// @OPTIMIZE: binary search
1311
static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
1312
9
{
1313
9
   stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
1314
9
   stbtt_uint32 tabledir = fontstart + 12;
1315
9
   stbtt_int32 i;
1316
67
   for (i=0; i < num_tables; ++i) {
1317
65
      stbtt_uint32 loc = tabledir + 16*i;
1318
65
      if (stbtt_tag(data+loc+0, tag))
1319
7
         return ttULONG(data+loc+8);
1320
65
   }
1321
2
   return 0;
1322
9
}
1323
1324
static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)
1325
1
{
1326
   // if it's just a font, there's only one valid index
1327
1
   if (stbtt__isfont(font_collection))
1328
1
      return index == 0 ? 0 : -1;
1329
1330
   // check if it's a TTC
1331
0
   if (stbtt_tag(font_collection, "ttcf")) {
1332
      // version 1?
1333
0
      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
1334
0
         stbtt_int32 n = ttLONG(font_collection+8);
1335
0
         if (index >= n)
1336
0
            return -1;
1337
0
         return ttULONG(font_collection+12+index*4);
1338
0
      }
1339
0
   }
1340
0
   return -1;
1341
0
}
1342
1343
static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
1344
0
{
1345
0
   // if it's just a font, there's only one valid font
1346
0
   if (stbtt__isfont(font_collection))
1347
0
      return 1;
1348
0
1349
0
   // check if it's a TTC
1350
0
   if (stbtt_tag(font_collection, "ttcf")) {
1351
0
      // version 1?
1352
0
      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
1353
0
         return ttLONG(font_collection+8);
1354
0
      }
1355
0
   }
1356
0
   return 0;
1357
0
}
1358
1359
static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
1360
0
{
1361
0
   stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
1362
0
   stbtt__buf pdict;
1363
0
   stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
1364
0
   if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
1365
0
   pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
1366
0
   stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
1367
0
   if (!subrsoff) return stbtt__new_buf(NULL, 0);
1368
0
   stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
1369
0
   return stbtt__cff_get_index(&cff);
1370
0
}
1371
1372
// since most people won't use this, find this table the first time it's needed
1373
static int stbtt__get_svg(stbtt_fontinfo *info)
1374
0
{
1375
0
   stbtt_uint32 t;
1376
0
   if (info->svg < 0) {
1377
0
      t = stbtt__find_table(info->data, info->fontstart, "SVG ");
1378
0
      if (t) {
1379
0
         stbtt_uint32 offset = ttULONG(info->data + t + 2);
1380
0
         info->svg = t + offset;
1381
0
      } else {
1382
0
         info->svg = 0;
1383
0
      }
1384
0
   }
1385
0
   return info->svg;
1386
0
}
1387
1388
static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
1389
1
{
1390
1
   stbtt_uint32 cmap, t;
1391
1
   stbtt_int32 i,numTables;
1392
1393
1
   info->data = data;
1394
1
   info->fontstart = fontstart;
1395
1
   info->cff = stbtt__new_buf(NULL, 0);
1396
1397
1
   cmap = stbtt__find_table(data, fontstart, "cmap");       // required
1398
1
   info->loca = stbtt__find_table(data, fontstart, "loca"); // required
1399
1
   info->head = stbtt__find_table(data, fontstart, "head"); // required
1400
1
   info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
1401
1
   info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
1402
1
   info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
1403
1
   info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
1404
1
   info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
1405
1406
1
   if (!cmap || !info->head || !info->hhea || !info->hmtx)
1407
0
      return 0;
1408
1
   if (info->glyf) {
1409
      // required for truetype
1410
1
      if (!info->loca) return 0;
1411
1
   } else {
1412
      // initialization for CFF / Type2 fonts (OTF)
1413
0
      stbtt__buf b, topdict, topdictidx;
1414
0
      stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
1415
0
      stbtt_uint32 cff;
1416
1417
0
      cff = stbtt__find_table(data, fontstart, "CFF ");
1418
0
      if (!cff) return 0;
1419
1420
0
      info->fontdicts = stbtt__new_buf(NULL, 0);
1421
0
      info->fdselect = stbtt__new_buf(NULL, 0);
1422
1423
      // @TODO this should use size from table (not 512MB)
1424
0
      info->cff = stbtt__new_buf(data+cff, 512*1024*1024);
1425
0
      b = info->cff;
1426
1427
      // read the header
1428
0
      stbtt__buf_skip(&b, 2);
1429
0
      stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize
1430
1431
      // @TODO the name INDEX could list multiple fonts,
1432
      // but we just use the first one.
1433
0
      stbtt__cff_get_index(&b);  // name INDEX
1434
0
      topdictidx = stbtt__cff_get_index(&b);
1435
0
      topdict = stbtt__cff_index_get(topdictidx, 0);
1436
0
      stbtt__cff_get_index(&b);  // string INDEX
1437
0
      info->gsubrs = stbtt__cff_get_index(&b);
1438
1439
0
      stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
1440
0
      stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
1441
0
      stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
1442
0
      stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
1443
0
      info->subrs = stbtt__get_subrs(b, topdict);
1444
1445
      // we only support Type 2 charstrings
1446
0
      if (cstype != 2) return 0;
1447
0
      if (charstrings == 0) return 0;
1448
1449
0
      if (fdarrayoff) {
1450
         // looks like a CID font
1451
0
         if (!fdselectoff) return 0;
1452
0
         stbtt__buf_seek(&b, fdarrayoff);
1453
0
         info->fontdicts = stbtt__cff_get_index(&b);
1454
0
         info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);
1455
0
      }
1456
1457
0
      stbtt__buf_seek(&b, charstrings);
1458
0
      info->charstrings = stbtt__cff_get_index(&b);
1459
0
   }
1460
1461
1
   t = stbtt__find_table(data, fontstart, "maxp");
1462
1
   if (t)
1463
1
      info->numGlyphs = ttUSHORT(data+t+4);
1464
0
   else
1465
0
      info->numGlyphs = 0xffff;
1466
1467
1
   info->svg = -1;
1468
1469
   // find a cmap encoding table we understand *now* to avoid searching
1470
   // later. (todo: could make this installable)
1471
   // the same regardless of glyph.
1472
1
   numTables = ttUSHORT(data + cmap + 2);
1473
1
   info->index_map = 0;
1474
4
   for (i=0; i < numTables; ++i) {
1475
3
      stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
1476
      // find an encoding we understand:
1477
3
      switch(ttUSHORT(data+encoding_record)) {
1478
1
         case STBTT_PLATFORM_ID_MICROSOFT:
1479
1
            switch (ttUSHORT(data+encoding_record+2)) {
1480
1
               case STBTT_MS_EID_UNICODE_BMP:
1481
1
               case STBTT_MS_EID_UNICODE_FULL:
1482
                  // MS/Unicode
1483
1
                  info->index_map = cmap + ttULONG(data+encoding_record+4);
1484
1
                  break;
1485
1
            }
1486
1
            break;
1487
1
        case STBTT_PLATFORM_ID_UNICODE:
1488
            // Mac/iOS has these
1489
            // all the encodingIDs are unicode, so we don't bother to check it
1490
1
            info->index_map = cmap + ttULONG(data+encoding_record+4);
1491
1
            break;
1492
3
      }
1493
3
   }
1494
1
   if (info->index_map == 0)
1495
0
      return 0;
1496
1497
1
   info->indexToLocFormat = ttUSHORT(data+info->head + 50);
1498
1
   return 1;
1499
1
}
1500
1501
STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
1502
25
{
1503
25
   stbtt_uint8 *data = info->data;
1504
25
   stbtt_uint32 index_map = info->index_map;
1505
1506
25
   stbtt_uint16 format = ttUSHORT(data + index_map + 0);
1507
25
   if (format == 0) { // apple byte encoding
1508
0
      stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
1509
0
      if (unicode_codepoint < bytes-6)
1510
0
         return ttBYTE(data + index_map + 6 + unicode_codepoint);
1511
0
      return 0;
1512
25
   } else if (format == 6) {
1513
0
      stbtt_uint32 first = ttUSHORT(data + index_map + 6);
1514
0
      stbtt_uint32 count = ttUSHORT(data + index_map + 8);
1515
0
      if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
1516
0
         return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
1517
0
      return 0;
1518
25
   } else if (format == 2) {
1519
0
      STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
1520
0
      return 0;
1521
25
   } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
1522
25
      stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
1523
25
      stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
1524
25
      stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
1525
25
      stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
1526
1527
      // do a binary search of the segments
1528
25
      stbtt_uint32 endCount = index_map + 14;
1529
25
      stbtt_uint32 search = endCount;
1530
1531
25
      if (unicode_codepoint > 0xffff)
1532
0
         return 0;
1533
1534
      // they lie from endCount .. endCount + segCount
1535
      // but searchRange is the nearest power of two, so...
1536
25
      if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
1537
2
         search += rangeShift*2;
1538
1539
      // now decrement to bias correctly to find smallest
1540
25
      search -= 2;
1541
75
      while (entrySelector) {
1542
50
         stbtt_uint16 end;
1543
50
         searchRange >>= 1;
1544
50
         end = ttUSHORT(data + search + searchRange*2);
1545
50
         if (unicode_codepoint > end)
1546
3
            search += searchRange*2;
1547
50
         --entrySelector;
1548
50
      }
1549
25
      search += 2;
1550
1551
25
      {
1552
25
         stbtt_uint16 offset, start, last;
1553
25
         stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
1554
1555
25
         start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
1556
25
         last = ttUSHORT(data + endCount + 2*item);
1557
25
         if (unicode_codepoint < start || unicode_codepoint > last)
1558
1
            return 0;
1559
1560
24
         offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
1561
24
         if (offset == 0)
1562
24
            return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
1563
1564
0
         return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
1565
24
      }
1566
24
   } else if (format == 12 || format == 13) {
1567
0
      stbtt_uint32 ngroups = ttULONG(data+index_map+12);
1568
0
      stbtt_int32 low,high;
1569
0
      low = 0; high = (stbtt_int32)ngroups;
1570
      // Binary search the right group.
1571
0
      while (low < high) {
1572
0
         stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
1573
0
         stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
1574
0
         stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
1575
0
         if ((stbtt_uint32) unicode_codepoint < start_char)
1576
0
            high = mid;
1577
0
         else if ((stbtt_uint32) unicode_codepoint > end_char)
1578
0
            low = mid+1;
1579
0
         else {
1580
0
            stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
1581
0
            if (format == 12)
1582
0
               return start_glyph + unicode_codepoint-start_char;
1583
0
            else // format == 13
1584
0
               return start_glyph;
1585
0
         }
1586
0
      }
1587
0
      return 0; // not found
1588
0
   }
1589
   // @TODO
1590
0
   STBTT_assert(0);
1591
0
   return 0;
1592
0
}
1593
1594
STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
1595
0
{
1596
0
   return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
1597
0
}
1598
1599
static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
1600
1.59k
{
1601
1.59k
   v->type = type;
1602
1.59k
   v->x = (stbtt_int16) x;
1603
1.59k
   v->y = (stbtt_int16) y;
1604
1.59k
   v->cx = (stbtt_int16) cx;
1605
1.59k
   v->cy = (stbtt_int16) cy;
1606
1.59k
}
1607
1608
static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
1609
82
{
1610
82
   int g1,g2;
1611
1612
82
   STBTT_assert(!info->cff.size);
1613
1614
82
   if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
1615
82
   if (info->indexToLocFormat >= 2)    return -1; // unknown index->glyph map format
1616
1617
82
   if (info->indexToLocFormat == 0) {
1618
82
      g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
1619
82
      g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
1620
82
   } else {
1621
0
      g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
1622
0
      g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
1623
0
   }
1624
1625
82
   return g1==g2 ? -1 : g1; // if length is 0, return -1
1626
82
}
1627
1628
static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
1629
1630
STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
1631
62
{
1632
62
   if (info->cff.size) {
1633
0
      stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
1634
62
   } else {
1635
62
      int g = stbtt__GetGlyfOffset(info, glyph_index);
1636
62
      if (g < 0) return 0;
1637
1638
60
      if (x0) *x0 = ttSHORT(info->data + g + 2);
1639
60
      if (y0) *y0 = ttSHORT(info->data + g + 4);
1640
60
      if (x1) *x1 = ttSHORT(info->data + g + 6);
1641
60
      if (y1) *y1 = ttSHORT(info->data + g + 8);
1642
60
   }
1643
60
   return 1;
1644
62
}
1645
1646
STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
1647
0
{
1648
0
   return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
1649
0
}
1650
1651
STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
1652
0
{
1653
0
   stbtt_int16 numberOfContours;
1654
0
   int g;
1655
0
   if (info->cff.size)
1656
0
      return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;
1657
0
   g = stbtt__GetGlyfOffset(info, glyph_index);
1658
0
   if (g < 0) return 1;
1659
0
   numberOfContours = ttSHORT(info->data + g);
1660
0
   return numberOfContours == 0;
1661
0
}
1662
1663
static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
1664
    stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
1665
319
{
1666
319
   if (start_off) {
1667
0
      if (was_off)
1668
0
         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
1669
0
      stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
1670
319
   } else {
1671
319
      if (was_off)
1672
0
         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
1673
319
      else
1674
319
         stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
1675
319
   }
1676
319
   return num_vertices;
1677
319
}
1678
1679
static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
1680
20
{
1681
20
   stbtt_int16 numberOfContours;
1682
20
   stbtt_uint8 *endPtsOfContours;
1683
20
   stbtt_uint8 *data = info->data;
1684
20
   stbtt_vertex *vertices=0;
1685
20
   int num_vertices=0;
1686
20
   int g = stbtt__GetGlyfOffset(info, glyph_index);
1687
1688
20
   *pvertices = NULL;
1689
1690
20
   if (g < 0) return 0;
1691
1692
20
   numberOfContours = ttSHORT(data + g);
1693
1694
20
   if (numberOfContours > 0) {
1695
20
      stbtt_uint8 flags=0,flagcount;
1696
20
      stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
1697
20
      stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
1698
20
      stbtt_uint8 *points;
1699
20
      endPtsOfContours = (data + g + 10);
1700
20
      ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
1701
20
      points = data + g + 10 + numberOfContours * 2 + 2 + ins;
1702
1703
20
      n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
1704
1705
20
      m = n + 2*numberOfContours;  // a loose bound on how many vertices we might need
1706
20
      vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
1707
20
      if (vertices == 0)
1708
0
         return 0;
1709
1710
20
      next_move = 0;
1711
20
      flagcount=0;
1712
1713
      // in first pass, we load uninterpreted data into the allocated array
1714
      // above, shifted to the end of the array so we won't overwrite it when
1715
      // we create our final data starting from the front
1716
1717
20
      off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
1718
1719
      // first load flags
1720
1721
1.29k
      for (i=0; i < n; ++i) {
1722
1.27k
         if (flagcount == 0) {
1723
1.27k
            flags = *points++;
1724
1.27k
            if (flags & 8)
1725
1
               flagcount = *points++;
1726
1.27k
         } else
1727
1
            --flagcount;
1728
1.27k
         vertices[off+i].type = flags;
1729
1.27k
      }
1730
1731
      // now load x coordinates
1732
20
      x=0;
1733
1.29k
      for (i=0; i < n; ++i) {
1734
1.27k
         flags = vertices[off+i].type;
1735
1.27k
         if (flags & 2) {
1736
389
            stbtt_int16 dx = *points++;
1737
389
            x += (flags & 16) ? dx : -dx; // ???
1738
887
         } else {
1739
887
            if (!(flags & 16)) {
1740
151
               x = x + (stbtt_int16) (points[0]*256 + points[1]);
1741
151
               points += 2;
1742
151
            }
1743
887
         }
1744
1.27k
         vertices[off+i].x = (stbtt_int16) x;
1745
1.27k
      }
1746
1747
      // now load y coordinates
1748
20
      y=0;
1749
1.29k
      for (i=0; i < n; ++i) {
1750
1.27k
         flags = vertices[off+i].type;
1751
1.27k
         if (flags & 4) {
1752
768
            stbtt_int16 dy = *points++;
1753
768
            y += (flags & 32) ? dy : -dy; // ???
1754
768
         } else {
1755
508
            if (!(flags & 32)) {
1756
21
               y = y + (stbtt_int16) (points[0]*256 + points[1]);
1757
21
               points += 2;
1758
21
            }
1759
508
         }
1760
1.27k
         vertices[off+i].y = (stbtt_int16) y;
1761
1.27k
      }
1762
1763
      // now convert them to our format
1764
20
      num_vertices=0;
1765
20
      sx = sy = cx = cy = scx = scy = 0;
1766
1.29k
      for (i=0; i < n; ++i) {
1767
1.27k
         flags = vertices[off+i].type;
1768
1.27k
         x     = (stbtt_int16) vertices[off+i].x;
1769
1.27k
         y     = (stbtt_int16) vertices[off+i].y;
1770
1771
1.27k
         if (next_move == i) {
1772
319
            if (i != 0)
1773
299
               num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
1774
1775
            // now start the new one
1776
319
            start_off = !(flags & 1);
1777
319
            if (start_off) {
1778
               // if we start off with an off-curve point, then when we need to find a point on the curve
1779
               // where we can start, and we need to save some state for when we wraparound.
1780
0
               scx = x;
1781
0
               scy = y;
1782
0
               if (!(vertices[off+i+1].type & 1)) {
1783
                  // next point is also a curve point, so interpolate an on-point curve
1784
0
                  sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
1785
0
                  sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
1786
0
               } else {
1787
                  // otherwise just use the next point as our start point
1788
0
                  sx = (stbtt_int32) vertices[off+i+1].x;
1789
0
                  sy = (stbtt_int32) vertices[off+i+1].y;
1790
0
                  ++i; // we're using point i+1 as the starting point, so skip it
1791
0
               }
1792
319
            } else {
1793
319
               sx = x;
1794
319
               sy = y;
1795
319
            }
1796
319
            stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
1797
319
            was_off = 0;
1798
319
            next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
1799
319
            ++j;
1800
957
         } else {
1801
957
            if (!(flags & 1)) { // if it's a curve
1802
0
               if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
1803
0
                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
1804
0
               cx = x;
1805
0
               cy = y;
1806
0
               was_off = 1;
1807
957
            } else {
1808
957
               if (was_off)
1809
0
                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
1810
957
               else
1811
957
                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
1812
957
               was_off = 0;
1813
957
            }
1814
957
         }
1815
1.27k
      }
1816
20
      num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
1817
20
   } else if (numberOfContours < 0) {
1818
      // Compound shapes.
1819
0
      int more = 1;
1820
0
      stbtt_uint8 *comp = data + g + 10;
1821
0
      num_vertices = 0;
1822
0
      vertices = 0;
1823
0
      while (more) {
1824
0
         stbtt_uint16 flags, gidx;
1825
0
         int comp_num_verts = 0, i;
1826
0
         stbtt_vertex *comp_verts = 0, *tmp = 0;
1827
0
         float mtx[6] = {1,0,0,1,0,0}, m, n;
1828
1829
0
         flags = ttSHORT(comp); comp+=2;
1830
0
         gidx = ttSHORT(comp); comp+=2;
1831
1832
0
         if (flags & 2) { // XY values
1833
0
            if (flags & 1) { // shorts
1834
0
               mtx[4] = ttSHORT(comp); comp+=2;
1835
0
               mtx[5] = ttSHORT(comp); comp+=2;
1836
0
            } else {
1837
0
               mtx[4] = ttCHAR(comp); comp+=1;
1838
0
               mtx[5] = ttCHAR(comp); comp+=1;
1839
0
            }
1840
0
         }
1841
0
         else {
1842
            // @TODO handle matching point
1843
0
            STBTT_assert(0);
1844
0
         }
1845
0
         if (flags & (1<<3)) { // WE_HAVE_A_SCALE
1846
0
            mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
1847
0
            mtx[1] = mtx[2] = 0;
1848
0
         } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
1849
0
            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
1850
0
            mtx[1] = mtx[2] = 0;
1851
0
            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
1852
0
         } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
1853
0
            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
1854
0
            mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
1855
0
            mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
1856
0
            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
1857
0
         }
1858
1859
         // Find transformation scales.
1860
0
         m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
1861
0
         n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
1862
1863
         // Get indexed glyph.
1864
0
         comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
1865
0
         if (comp_num_verts > 0) {
1866
            // Transform vertices.
1867
0
            for (i = 0; i < comp_num_verts; ++i) {
1868
0
               stbtt_vertex* v = &comp_verts[i];
1869
0
               stbtt_vertex_type x,y;
1870
0
               x=v->x; y=v->y;
1871
0
               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
1872
0
               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
1873
0
               x=v->cx; y=v->cy;
1874
0
               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
1875
0
               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
1876
0
            }
1877
            // Append vertices.
1878
0
            tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
1879
0
            if (!tmp) {
1880
0
               if (vertices) STBTT_free(vertices, info->userdata);
1881
0
               if (comp_verts) STBTT_free(comp_verts, info->userdata);
1882
0
               return 0;
1883
0
            }
1884
0
            if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
1885
0
            STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
1886
0
            if (vertices) STBTT_free(vertices, info->userdata);
1887
0
            vertices = tmp;
1888
0
            STBTT_free(comp_verts, info->userdata);
1889
0
            num_vertices += comp_num_verts;
1890
0
         }
1891
         // More components ?
1892
0
         more = flags & (1<<5);
1893
0
      }
1894
0
   } else {
1895
      // numberOfCounters == 0, do nothing
1896
0
   }
1897
1898
20
   *pvertices = vertices;
1899
20
   return num_vertices;
1900
20
}
1901
1902
typedef struct
1903
{
1904
   int bounds;
1905
   int started;
1906
   float first_x, first_y;
1907
   float x, y;
1908
   stbtt_int32 min_x, max_x, min_y, max_y;
1909
1910
   stbtt_vertex *pvertices;
1911
   int num_vertices;
1912
} stbtt__csctx;
1913
1914
0
#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
1915
1916
static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)
1917
0
{
1918
0
   if (x > c->max_x || !c->started) c->max_x = x;
1919
0
   if (y > c->max_y || !c->started) c->max_y = y;
1920
0
   if (x < c->min_x || !c->started) c->min_x = x;
1921
0
   if (y < c->min_y || !c->started) c->min_y = y;
1922
0
   c->started = 1;
1923
0
}
1924
1925
static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)
1926
0
{
1927
0
   if (c->bounds) {
1928
0
      stbtt__track_vertex(c, x, y);
1929
0
      if (type == STBTT_vcubic) {
1930
0
         stbtt__track_vertex(c, cx, cy);
1931
0
         stbtt__track_vertex(c, cx1, cy1);
1932
0
      }
1933
0
   } else {
1934
0
      stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
1935
0
      c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;
1936
0
      c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;
1937
0
   }
1938
0
   c->num_vertices++;
1939
0
}
1940
1941
static void stbtt__csctx_close_shape(stbtt__csctx *ctx)
1942
0
{
1943
0
   if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
1944
0
      stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
1945
0
}
1946
1947
static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)
1948
0
{
1949
0
   stbtt__csctx_close_shape(ctx);
1950
0
   ctx->first_x = ctx->x = ctx->x + dx;
1951
0
   ctx->first_y = ctx->y = ctx->y + dy;
1952
0
   stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
1953
0
}
1954
1955
static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)
1956
0
{
1957
0
   ctx->x += dx;
1958
0
   ctx->y += dy;
1959
0
   stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
1960
0
}
1961
1962
static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
1963
0
{
1964
0
   float cx1 = ctx->x + dx1;
1965
0
   float cy1 = ctx->y + dy1;
1966
0
   float cx2 = cx1 + dx2;
1967
0
   float cy2 = cy1 + dy2;
1968
0
   ctx->x = cx2 + dx3;
1969
0
   ctx->y = cy2 + dy3;
1970
0
   stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
1971
0
}
1972
1973
static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)
1974
0
{
1975
0
   int count = stbtt__cff_index_count(&idx);
1976
0
   int bias = 107;
1977
0
   if (count >= 33900)
1978
0
      bias = 32768;
1979
0
   else if (count >= 1240)
1980
0
      bias = 1131;
1981
0
   n += bias;
1982
0
   if (n < 0 || n >= count)
1983
0
      return stbtt__new_buf(NULL, 0);
1984
0
   return stbtt__cff_index_get(idx, n);
1985
0
}
1986
1987
static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)
1988
0
{
1989
0
   stbtt__buf fdselect = info->fdselect;
1990
0
   int nranges, start, end, v, fmt, fdselector = -1, i;
1991
1992
0
   stbtt__buf_seek(&fdselect, 0);
1993
0
   fmt = stbtt__buf_get8(&fdselect);
1994
0
   if (fmt == 0) {
1995
      // untested
1996
0
      stbtt__buf_skip(&fdselect, glyph_index);
1997
0
      fdselector = stbtt__buf_get8(&fdselect);
1998
0
   } else if (fmt == 3) {
1999
0
      nranges = stbtt__buf_get16(&fdselect);
2000
0
      start = stbtt__buf_get16(&fdselect);
2001
0
      for (i = 0; i < nranges; i++) {
2002
0
         v = stbtt__buf_get8(&fdselect);
2003
0
         end = stbtt__buf_get16(&fdselect);
2004
0
         if (glyph_index >= start && glyph_index < end) {
2005
0
            fdselector = v;
2006
0
            break;
2007
0
         }
2008
0
         start = end;
2009
0
      }
2010
0
   }
2011
0
   if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422
2012
0
   return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
2013
0
}
2014
2015
static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)
2016
0
{
2017
0
   int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
2018
0
   int has_subrs = 0, clear_stack;
2019
0
   float s[48];
2020
0
   stbtt__buf subr_stack[10], subrs = info->subrs, b;
2021
0
   float f;
2022
2023
0
#define STBTT__CSERR(s) (0)
2024
2025
   // this currently ignores the initial width value, which isn't needed if we have hmtx
2026
0
   b = stbtt__cff_index_get(info->charstrings, glyph_index);
2027
0
   while (b.cursor < b.size) {
2028
0
      i = 0;
2029
0
      clear_stack = 1;
2030
0
      b0 = stbtt__buf_get8(&b);
2031
0
      switch (b0) {
2032
      // @TODO implement hinting
2033
0
      case 0x13: // hintmask
2034
0
      case 0x14: // cntrmask
2035
0
         if (in_header)
2036
0
            maskbits += (sp / 2); // implicit "vstem"
2037
0
         in_header = 0;
2038
0
         stbtt__buf_skip(&b, (maskbits + 7) / 8);
2039
0
         break;
2040
2041
0
      case 0x01: // hstem
2042
0
      case 0x03: // vstem
2043
0
      case 0x12: // hstemhm
2044
0
      case 0x17: // vstemhm
2045
0
         maskbits += (sp / 2);
2046
0
         break;
2047
2048
0
      case 0x15: // rmoveto
2049
0
         in_header = 0;
2050
0
         if (sp < 2) return STBTT__CSERR("rmoveto stack");
2051
0
         stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
2052
0
         break;
2053
0
      case 0x04: // vmoveto
2054
0
         in_header = 0;
2055
0
         if (sp < 1) return STBTT__CSERR("vmoveto stack");
2056
0
         stbtt__csctx_rmove_to(c, 0, s[sp-1]);
2057
0
         break;
2058
0
      case 0x16: // hmoveto
2059
0
         in_header = 0;
2060
0
         if (sp < 1) return STBTT__CSERR("hmoveto stack");
2061
0
         stbtt__csctx_rmove_to(c, s[sp-1], 0);
2062
0
         break;
2063
2064
0
      case 0x05: // rlineto
2065
0
         if (sp < 2) return STBTT__CSERR("rlineto stack");
2066
0
         for (; i + 1 < sp; i += 2)
2067
0
            stbtt__csctx_rline_to(c, s[i], s[i+1]);
2068
0
         break;
2069
2070
      // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
2071
      // starting from a different place.
2072
2073
0
      case 0x07: // vlineto
2074
0
         if (sp < 1) return STBTT__CSERR("vlineto stack");
2075
0
         goto vlineto;
2076
0
      case 0x06: // hlineto
2077
0
         if (sp < 1) return STBTT__CSERR("hlineto stack");
2078
0
         for (;;) {
2079
0
            if (i >= sp) break;
2080
0
            stbtt__csctx_rline_to(c, s[i], 0);
2081
0
            i++;
2082
0
      vlineto:
2083
0
            if (i >= sp) break;
2084
0
            stbtt__csctx_rline_to(c, 0, s[i]);
2085
0
            i++;
2086
0
         }
2087
0
         break;
2088
2089
0
      case 0x1F: // hvcurveto
2090
0
         if (sp < 4) return STBTT__CSERR("hvcurveto stack");
2091
0
         goto hvcurveto;
2092
0
      case 0x1E: // vhcurveto
2093
0
         if (sp < 4) return STBTT__CSERR("vhcurveto stack");
2094
0
         for (;;) {
2095
0
            if (i + 3 >= sp) break;
2096
0
            stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
2097
0
            i += 4;
2098
0
      hvcurveto:
2099
0
            if (i + 3 >= sp) break;
2100
0
            stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
2101
0
            i += 4;
2102
0
         }
2103
0
         break;
2104
2105
0
      case 0x08: // rrcurveto
2106
0
         if (sp < 6) return STBTT__CSERR("rcurveline stack");
2107
0
         for (; i + 5 < sp; i += 6)
2108
0
            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
2109
0
         break;
2110
2111
0
      case 0x18: // rcurveline
2112
0
         if (sp < 8) return STBTT__CSERR("rcurveline stack");
2113
0
         for (; i + 5 < sp - 2; i += 6)
2114
0
            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
2115
0
         if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack");
2116
0
         stbtt__csctx_rline_to(c, s[i], s[i+1]);
2117
0
         break;
2118
2119
0
      case 0x19: // rlinecurve
2120
0
         if (sp < 8) return STBTT__CSERR("rlinecurve stack");
2121
0
         for (; i + 1 < sp - 6; i += 2)
2122
0
            stbtt__csctx_rline_to(c, s[i], s[i+1]);
2123
0
         if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack");
2124
0
         stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
2125
0
         break;
2126
2127
0
      case 0x1A: // vvcurveto
2128
0
      case 0x1B: // hhcurveto
2129
0
         if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack");
2130
0
         f = 0.0;
2131
0
         if (sp & 1) { f = s[i]; i++; }
2132
0
         for (; i + 3 < sp; i += 4) {
2133
0
            if (b0 == 0x1B)
2134
0
               stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
2135
0
            else
2136
0
               stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
2137
0
            f = 0.0;
2138
0
         }
2139
0
         break;
2140
2141
0
      case 0x0A: // callsubr
2142
0
         if (!has_subrs) {
2143
0
            if (info->fdselect.size)
2144
0
               subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
2145
0
            has_subrs = 1;
2146
0
         }
2147
         // FALLTHROUGH
2148
0
      case 0x1D: // callgsubr
2149
0
         if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
2150
0
         v = (int) s[--sp];
2151
0
         if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit");
2152
0
         subr_stack[subr_stack_height++] = b;
2153
0
         b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
2154
0
         if (b.size == 0) return STBTT__CSERR("subr not found");
2155
0
         b.cursor = 0;
2156
0
         clear_stack = 0;
2157
0
         break;
2158
2159
0
      case 0x0B: // return
2160
0
         if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr");
2161
0
         b = subr_stack[--subr_stack_height];
2162
0
         clear_stack = 0;
2163
0
         break;
2164
2165
0
      case 0x0E: // endchar
2166
0
         stbtt__csctx_close_shape(c);
2167
0
         return 1;
2168
2169
0
      case 0x0C: { // two-byte escape
2170
0
         float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
2171
0
         float dx, dy;
2172
0
         int b1 = stbtt__buf_get8(&b);
2173
0
         switch (b1) {
2174
         // @TODO These "flex" implementations ignore the flex-depth and resolution,
2175
         // and always draw beziers.
2176
0
         case 0x22: // hflex
2177
0
            if (sp < 7) return STBTT__CSERR("hflex stack");
2178
0
            dx1 = s[0];
2179
0
            dx2 = s[1];
2180
0
            dy2 = s[2];
2181
0
            dx3 = s[3];
2182
0
            dx4 = s[4];
2183
0
            dx5 = s[5];
2184
0
            dx6 = s[6];
2185
0
            stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
2186
0
            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
2187
0
            break;
2188
2189
0
         case 0x23: // flex
2190
0
            if (sp < 13) return STBTT__CSERR("flex stack");
2191
0
            dx1 = s[0];
2192
0
            dy1 = s[1];
2193
0
            dx2 = s[2];
2194
0
            dy2 = s[3];
2195
0
            dx3 = s[4];
2196
0
            dy3 = s[5];
2197
0
            dx4 = s[6];
2198
0
            dy4 = s[7];
2199
0
            dx5 = s[8];
2200
0
            dy5 = s[9];
2201
0
            dx6 = s[10];
2202
0
            dy6 = s[11];
2203
            //fd is s[12]
2204
0
            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
2205
0
            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
2206
0
            break;
2207
2208
0
         case 0x24: // hflex1
2209
0
            if (sp < 9) return STBTT__CSERR("hflex1 stack");
2210
0
            dx1 = s[0];
2211
0
            dy1 = s[1];
2212
0
            dx2 = s[2];
2213
0
            dy2 = s[3];
2214
0
            dx3 = s[4];
2215
0
            dx4 = s[5];
2216
0
            dx5 = s[6];
2217
0
            dy5 = s[7];
2218
0
            dx6 = s[8];
2219
0
            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
2220
0
            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
2221
0
            break;
2222
2223
0
         case 0x25: // flex1
2224
0
            if (sp < 11) return STBTT__CSERR("flex1 stack");
2225
0
            dx1 = s[0];
2226
0
            dy1 = s[1];
2227
0
            dx2 = s[2];
2228
0
            dy2 = s[3];
2229
0
            dx3 = s[4];
2230
0
            dy3 = s[5];
2231
0
            dx4 = s[6];
2232
0
            dy4 = s[7];
2233
0
            dx5 = s[8];
2234
0
            dy5 = s[9];
2235
0
            dx6 = dy6 = s[10];
2236
0
            dx = dx1+dx2+dx3+dx4+dx5;
2237
0
            dy = dy1+dy2+dy3+dy4+dy5;
2238
0
            if (STBTT_fabs(dx) > STBTT_fabs(dy))
2239
0
               dy6 = -dy;
2240
0
            else
2241
0
               dx6 = -dx;
2242
0
            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
2243
0
            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
2244
0
            break;
2245
2246
0
         default:
2247
0
            return STBTT__CSERR("unimplemented");
2248
0
         }
2249
0
      } break;
2250
2251
0
      default:
2252
0
         if (b0 != 255 && b0 != 28 && b0 < 32)
2253
0
            return STBTT__CSERR("reserved operator");
2254
2255
         // push immediate
2256
0
         if (b0 == 255) {
2257
0
            f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
2258
0
         } else {
2259
0
            stbtt__buf_skip(&b, -1);
2260
0
            f = (float)(stbtt_int16)stbtt__cff_int(&b);
2261
0
         }
2262
0
         if (sp >= 48) return STBTT__CSERR("push stack overflow");
2263
0
         s[sp++] = f;
2264
0
         clear_stack = 0;
2265
0
         break;
2266
0
      }
2267
0
      if (clear_stack) sp = 0;
2268
0
   }
2269
0
   return STBTT__CSERR("no endchar");
2270
2271
0
#undef STBTT__CSERR
2272
0
}
2273
2274
static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
2275
0
{
2276
   // runs the charstring twice, once to count and once to output (to avoid realloc)
2277
0
   stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);
2278
0
   stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);
2279
0
   if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {
2280
0
      *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);
2281
0
      output_ctx.pvertices = *pvertices;
2282
0
      if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {
2283
0
         STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);
2284
0
         return output_ctx.num_vertices;
2285
0
      }
2286
0
   }
2287
0
   *pvertices = NULL;
2288
0
   return 0;
2289
0
}
2290
2291
static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
2292
0
{
2293
0
   stbtt__csctx c = STBTT__CSCTX_INIT(1);
2294
0
   int r = stbtt__run_charstring(info, glyph_index, &c);
2295
0
   if (x0)  *x0 = r ? c.min_x : 0;
2296
0
   if (y0)  *y0 = r ? c.min_y : 0;
2297
0
   if (x1)  *x1 = r ? c.max_x : 0;
2298
0
   if (y1)  *y1 = r ? c.max_y : 0;
2299
0
   return r ? c.num_vertices : 0;
2300
0
}
2301
2302
STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
2303
20
{
2304
20
   if (!info->cff.size)
2305
20
      return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
2306
0
   else
2307
0
      return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
2308
20
}
2309
2310
STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
2311
22
{
2312
22
   stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
2313
22
   if (glyph_index < numOfLongHorMetrics) {
2314
0
      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*glyph_index);
2315
0
      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
2316
22
   } else {
2317
22
      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
2318
22
      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
2319
22
   }
2320
22
}
2321
2322
STBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info)
2323
0
{
2324
0
   stbtt_uint8 *data = info->data + info->kern;
2325
0
2326
0
   // we only look at the first table. it must be 'horizontal' and format 0.
2327
0
   if (!info->kern)
2328
0
      return 0;
2329
0
   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
2330
0
      return 0;
2331
0
   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
2332
0
      return 0;
2333
0
2334
0
   return ttUSHORT(data+10);
2335
0
}
2336
2337
STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)
2338
0
{
2339
0
   stbtt_uint8 *data = info->data + info->kern;
2340
0
   int k, length;
2341
0
2342
0
   // we only look at the first table. it must be 'horizontal' and format 0.
2343
0
   if (!info->kern)
2344
0
      return 0;
2345
0
   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
2346
0
      return 0;
2347
0
   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
2348
0
      return 0;
2349
0
2350
0
   length = ttUSHORT(data+10);
2351
0
   if (table_length < length)
2352
0
      length = table_length;
2353
0
2354
0
   for (k = 0; k < length; k++)
2355
0
   {
2356
0
      table[k].glyph1 = ttUSHORT(data+18+(k*6));
2357
0
      table[k].glyph2 = ttUSHORT(data+20+(k*6));
2358
0
      table[k].advance = ttSHORT(data+22+(k*6));
2359
0
   }
2360
0
2361
0
   return length;
2362
0
}
2363
2364
static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
2365
0
{
2366
0
   stbtt_uint8 *data = info->data + info->kern;
2367
0
   stbtt_uint32 needle, straw;
2368
0
   int l, r, m;
2369
0
2370
0
   // we only look at the first table. it must be 'horizontal' and format 0.
2371
0
   if (!info->kern)
2372
0
      return 0;
2373
0
   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
2374
0
      return 0;
2375
0
   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
2376
0
      return 0;
2377
0
2378
0
   l = 0;
2379
0
   r = ttUSHORT(data+10) - 1;
2380
0
   needle = glyph1 << 16 | glyph2;
2381
0
   while (l <= r) {
2382
0
      m = (l + r) >> 1;
2383
0
      straw = ttULONG(data+18+(m*6)); // note: unaligned read
2384
0
      if (needle < straw)
2385
0
         r = m - 1;
2386
0
      else if (needle > straw)
2387
0
         l = m + 1;
2388
0
      else
2389
0
         return ttSHORT(data+22+(m*6));
2390
0
   }
2391
0
   return 0;
2392
0
}
2393
2394
static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
2395
0
{
2396
0
   stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
2397
0
   switch (coverageFormat) {
2398
0
      case 1: {
2399
0
         stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
2400
0
2401
0
         // Binary search.
2402
0
         stbtt_int32 l=0, r=glyphCount-1, m;
2403
0
         int straw, needle=glyph;
2404
0
         while (l <= r) {
2405
0
            stbtt_uint8 *glyphArray = coverageTable + 4;
2406
0
            stbtt_uint16 glyphID;
2407
0
            m = (l + r) >> 1;
2408
0
            glyphID = ttUSHORT(glyphArray + 2 * m);
2409
0
            straw = glyphID;
2410
0
            if (needle < straw)
2411
0
               r = m - 1;
2412
0
            else if (needle > straw)
2413
0
               l = m + 1;
2414
0
            else {
2415
0
               return m;
2416
0
            }
2417
0
         }
2418
0
         break;
2419
0
      }
2420
0
2421
0
      case 2: {
2422
0
         stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
2423
0
         stbtt_uint8 *rangeArray = coverageTable + 4;
2424
0
2425
0
         // Binary search.
2426
0
         stbtt_int32 l=0, r=rangeCount-1, m;
2427
0
         int strawStart, strawEnd, needle=glyph;
2428
0
         while (l <= r) {
2429
0
            stbtt_uint8 *rangeRecord;
2430
0
            m = (l + r) >> 1;
2431
0
            rangeRecord = rangeArray + 6 * m;
2432
0
            strawStart = ttUSHORT(rangeRecord);
2433
0
            strawEnd = ttUSHORT(rangeRecord + 2);
2434
0
            if (needle < strawStart)
2435
0
               r = m - 1;
2436
0
            else if (needle > strawEnd)
2437
0
               l = m + 1;
2438
0
            else {
2439
0
               stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
2440
0
               return startCoverageIndex + glyph - strawStart;
2441
0
            }
2442
0
         }
2443
0
         break;
2444
0
      }
2445
0
2446
0
      default: return -1; // unsupported
2447
0
   }
2448
0
2449
0
   return -1;
2450
0
}
2451
2452
static stbtt_int32  stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
2453
0
{
2454
0
   stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
2455
0
   switch (classDefFormat)
2456
0
   {
2457
0
      case 1: {
2458
0
         stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
2459
0
         stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
2460
0
         stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
2461
0
2462
0
         if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
2463
0
            return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
2464
0
         break;
2465
0
      }
2466
0
2467
0
      case 2: {
2468
0
         stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
2469
0
         stbtt_uint8 *classRangeRecords = classDefTable + 4;
2470
0
2471
0
         // Binary search.
2472
0
         stbtt_int32 l=0, r=classRangeCount-1, m;
2473
0
         int strawStart, strawEnd, needle=glyph;
2474
0
         while (l <= r) {
2475
0
            stbtt_uint8 *classRangeRecord;
2476
0
            m = (l + r) >> 1;
2477
0
            classRangeRecord = classRangeRecords + 6 * m;
2478
0
            strawStart = ttUSHORT(classRangeRecord);
2479
0
            strawEnd = ttUSHORT(classRangeRecord + 2);
2480
0
            if (needle < strawStart)
2481
0
               r = m - 1;
2482
0
            else if (needle > strawEnd)
2483
0
               l = m + 1;
2484
0
            else
2485
0
               return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
2486
0
         }
2487
0
         break;
2488
0
      }
2489
0
2490
0
      default:
2491
0
         return -1; // Unsupported definition type, return an error.
2492
0
   }
2493
0
2494
0
   // "All glyphs not assigned to a class fall into class 0". (OpenType spec)
2495
0
   return 0;
2496
0
}
2497
2498
// Define to STBTT_assert(x) if you want to break on unimplemented formats.
2499
#define STBTT_GPOS_TODO_assert(x)
2500
2501
static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
2502
0
{
2503
0
   stbtt_uint16 lookupListOffset;
2504
0
   stbtt_uint8 *lookupList;
2505
0
   stbtt_uint16 lookupCount;
2506
0
   stbtt_uint8 *data;
2507
0
   stbtt_int32 i, sti;
2508
0
2509
0
   if (!info->gpos) return 0;
2510
0
2511
0
   data = info->data + info->gpos;
2512
0
2513
0
   if (ttUSHORT(data+0) != 1) return 0; // Major version 1
2514
0
   if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
2515
0
2516
0
   lookupListOffset = ttUSHORT(data+8);
2517
0
   lookupList = data + lookupListOffset;
2518
0
   lookupCount = ttUSHORT(lookupList);
2519
0
2520
0
   for (i=0; i<lookupCount; ++i) {
2521
0
      stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
2522
0
      stbtt_uint8 *lookupTable = lookupList + lookupOffset;
2523
0
2524
0
      stbtt_uint16 lookupType = ttUSHORT(lookupTable);
2525
0
      stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
2526
0
      stbtt_uint8 *subTableOffsets = lookupTable + 6;
2527
0
      if (lookupType != 2) // Pair Adjustment Positioning Subtable
2528
0
         continue;
2529
0
2530
0
      for (sti=0; sti<subTableCount; sti++) {
2531
0
         stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
2532
0
         stbtt_uint8 *table = lookupTable + subtableOffset;
2533
0
         stbtt_uint16 posFormat = ttUSHORT(table);
2534
0
         stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
2535
0
         stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
2536
0
         if (coverageIndex == -1) continue;
2537
0
2538
0
         switch (posFormat) {
2539
0
            case 1: {
2540
0
               stbtt_int32 l, r, m;
2541
0
               int straw, needle;
2542
0
               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
2543
0
               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
2544
0
               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
2545
0
                  stbtt_int32 valueRecordPairSizeInBytes = 2;
2546
0
                  stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
2547
0
                  stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
2548
0
                  stbtt_uint8 *pairValueTable = table + pairPosOffset;
2549
0
                  stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
2550
0
                  stbtt_uint8 *pairValueArray = pairValueTable + 2;
2551
0
2552
0
                  if (coverageIndex >= pairSetCount) return 0;
2553
0
2554
0
                  needle=glyph2;
2555
0
                  r=pairValueCount-1;
2556
0
                  l=0;
2557
0
2558
0
                  // Binary search.
2559
0
                  while (l <= r) {
2560
0
                     stbtt_uint16 secondGlyph;
2561
0
                     stbtt_uint8 *pairValue;
2562
0
                     m = (l + r) >> 1;
2563
0
                     pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
2564
0
                     secondGlyph = ttUSHORT(pairValue);
2565
0
                     straw = secondGlyph;
2566
0
                     if (needle < straw)
2567
0
                        r = m - 1;
2568
0
                     else if (needle > straw)
2569
0
                        l = m + 1;
2570
0
                     else {
2571
0
                        stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
2572
0
                        return xAdvance;
2573
0
                     }
2574
0
                  }
2575
0
               } else
2576
0
                  return 0;
2577
0
               break;
2578
0
            }
2579
0
2580
0
            case 2: {
2581
0
               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
2582
0
               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
2583
0
               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
2584
0
                  stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
2585
0
                  stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
2586
0
                  int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
2587
0
                  int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
2588
0
2589
0
                  stbtt_uint16 class1Count = ttUSHORT(table + 12);
2590
0
                  stbtt_uint16 class2Count = ttUSHORT(table + 14);
2591
0
                  stbtt_uint8 *class1Records, *class2Records;
2592
0
                  stbtt_int16 xAdvance;
2593
0
2594
0
                  if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
2595
0
                  if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
2596
0
2597
0
                  class1Records = table + 16;
2598
0
                  class2Records = class1Records + 2 * (glyph1class * class2Count);
2599
0
                  xAdvance = ttSHORT(class2Records + 2 * glyph2class);
2600
0
                  return xAdvance;
2601
0
               } else
2602
0
                  return 0;
2603
0
               break;
2604
0
            }
2605
0
2606
0
            default:
2607
0
               return 0; // Unsupported position format
2608
0
         }
2609
0
      }
2610
0
   }
2611
0
2612
0
   return 0;
2613
0
}
2614
2615
STBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
2616
0
{
2617
0
   int xAdvance = 0;
2618
0
2619
0
   if (info->gpos)
2620
0
      xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
2621
0
   else if (info->kern)
2622
0
      xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
2623
0
2624
0
   return xAdvance;
2625
0
}
2626
2627
STBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
2628
0
{
2629
0
   if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
2630
0
      return 0;
2631
0
   return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
2632
0
}
2633
2634
STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
2635
0
{
2636
0
   stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
2637
0
}
2638
2639
STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
2640
1
{
2641
1
   if (ascent ) *ascent  = ttSHORT(info->data+info->hhea + 4);
2642
1
   if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
2643
1
   if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
2644
1
}
2645
2646
STBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)
2647
0
{
2648
0
   int tab = stbtt__find_table(info->data, info->fontstart, "OS/2");
2649
0
   if (!tab)
2650
0
      return 0;
2651
0
   if (typoAscent ) *typoAscent  = ttSHORT(info->data+tab + 68);
2652
0
   if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);
2653
0
   if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);
2654
0
   return 1;
2655
0
}
2656
2657
STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
2658
0
{
2659
0
   *x0 = ttSHORT(info->data + info->head + 36);
2660
0
   *y0 = ttSHORT(info->data + info->head + 38);
2661
0
   *x1 = ttSHORT(info->data + info->head + 40);
2662
0
   *y1 = ttSHORT(info->data + info->head + 42);
2663
0
}
2664
2665
STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
2666
1
{
2667
1
   int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
2668
1
   return (float) height / fheight;
2669
1
}
2670
2671
STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
2672
0
{
2673
0
   int unitsPerEm = ttUSHORT(info->data + info->head + 18);
2674
0
   return pixels / unitsPerEm;
2675
0
}
2676
2677
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
2678
0
{
2679
0
   STBTT_free(v, info->userdata);
2680
0
}
2681
2682
STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)
2683
0
{
2684
0
   int i;
2685
0
   stbtt_uint8 *data = info->data;
2686
0
   stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);
2687
0
2688
0
   int numEntries = ttUSHORT(svg_doc_list);
2689
0
   stbtt_uint8 *svg_docs = svg_doc_list + 2;
2690
0
2691
0
   for(i=0; i<numEntries; i++) {
2692
0
      stbtt_uint8 *svg_doc = svg_docs + (12 * i);
2693
0
      if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))
2694
0
         return svg_doc;
2695
0
   }
2696
0
   return 0;
2697
0
}
2698
2699
STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)
2700
0
{
2701
0
   stbtt_uint8 *data = info->data;
2702
0
   stbtt_uint8 *svg_doc;
2703
0
2704
0
   if (info->svg == 0)
2705
0
      return 0;
2706
0
2707
0
   svg_doc = stbtt_FindSVGDoc(info, gl);
2708
0
   if (svg_doc != NULL) {
2709
0
      *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);
2710
0
      return ttULONG(svg_doc + 8);
2711
0
   } else {
2712
0
      return 0;
2713
0
   }
2714
0
}
2715
2716
STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)
2717
0
{
2718
0
   return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);
2719
0
}
2720
2721
//////////////////////////////////////////////////////////////////////////////
2722
//
2723
// antialiasing software rasterizer
2724
//
2725
2726
STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
2727
62
{
2728
62
   int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
2729
62
   if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
2730
      // e.g. space character
2731
2
      if (ix0) *ix0 = 0;
2732
2
      if (iy0) *iy0 = 0;
2733
2
      if (ix1) *ix1 = 0;
2734
2
      if (iy1) *iy1 = 0;
2735
60
   } else {
2736
      // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
2737
60
      if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
2738
60
      if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
2739
60
      if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
2740
60
      if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
2741
60
   }
2742
62
}
2743
2744
STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
2745
20
{
2746
20
   stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
2747
20
}
2748
2749
STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
2750
0
{
2751
0
   stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
2752
0
}
2753
2754
STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
2755
0
{
2756
0
   stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
2757
0
}
2758
2759
//////////////////////////////////////////////////////////////////////////////
2760
//
2761
//  Rasterizer
2762
2763
typedef struct stbtt__hheap_chunk
2764
{
2765
   struct stbtt__hheap_chunk *next;
2766
} stbtt__hheap_chunk;
2767
2768
typedef struct stbtt__hheap
2769
{
2770
   struct stbtt__hheap_chunk *head;
2771
   void   *first_free;
2772
   int    num_remaining_in_head_chunk;
2773
} stbtt__hheap;
2774
2775
static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
2776
638
{
2777
638
   if (hh->first_free) {
2778
398
      void *p = hh->first_free;
2779
398
      hh->first_free = * (void **) p;
2780
398
      return p;
2781
398
   } else {
2782
240
      if (hh->num_remaining_in_head_chunk == 0) {
2783
20
         int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
2784
20
         stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
2785
20
         if (c == NULL)
2786
0
            return NULL;
2787
20
         c->next = hh->head;
2788
20
         hh->head = c;
2789
20
         hh->num_remaining_in_head_chunk = count;
2790
20
      }
2791
240
      --hh->num_remaining_in_head_chunk;
2792
240
      return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
2793
240
   }
2794
638
}
2795
2796
static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
2797
538
{
2798
538
   *(void **) p = hh->first_free;
2799
538
   hh->first_free = p;
2800
538
}
2801
2802
static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
2803
20
{
2804
20
   stbtt__hheap_chunk *c = hh->head;
2805
40
   while (c) {
2806
20
      stbtt__hheap_chunk *n = c->next;
2807
20
      STBTT_free(c, userdata);
2808
20
      c = n;
2809
20
   }
2810
20
}
2811
2812
typedef struct stbtt__edge {
2813
   float x0,y0, x1,y1;
2814
   int invert;
2815
} stbtt__edge;
2816
2817
2818
typedef struct stbtt__active_edge
2819
{
2820
   struct stbtt__active_edge *next;
2821
   #if STBTT_RASTERIZER_VERSION==1
2822
   int x,dx;
2823
   float ey;
2824
   int direction;
2825
   #elif STBTT_RASTERIZER_VERSION==2
2826
   float fx,fdx,fdy;
2827
   float direction;
2828
   float sy;
2829
   float ey;
2830
   #else
2831
   #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
2832
   #endif
2833
} stbtt__active_edge;
2834
2835
#if STBTT_RASTERIZER_VERSION == 1
2836
#define STBTT_FIXSHIFT   10
2837
#define STBTT_FIX        (1 << STBTT_FIXSHIFT)
2838
#define STBTT_FIXMASK    (STBTT_FIX-1)
2839
2840
static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
2841
{
2842
   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
2843
   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
2844
   STBTT_assert(z != NULL);
2845
   if (!z) return z;
2846
2847
   // round dx down to avoid overshooting
2848
   if (dxdy < 0)
2849
      z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
2850
   else
2851
      z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
2852
2853
   z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
2854
   z->x -= off_x * STBTT_FIX;
2855
2856
   z->ey = e->y1;
2857
   z->next = 0;
2858
   z->direction = e->invert ? 1 : -1;
2859
   return z;
2860
}
2861
#elif STBTT_RASTERIZER_VERSION == 2
2862
static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
2863
638
{
2864
638
   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
2865
638
   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
2866
638
   STBTT_assert(z != NULL);
2867
   //STBTT_assert(e->y0 <= start_point);
2868
638
   if (!z) return z;
2869
638
   z->fdx = dxdy;
2870
638
   z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
2871
638
   z->fx = e->x0 + dxdy * (start_point - e->y0);
2872
638
   z->fx -= off_x;
2873
638
   z->direction = e->invert ? 1.0f : -1.0f;
2874
638
   z->sy = e->y0;
2875
638
   z->ey = e->y1;
2876
638
   z->next = 0;
2877
638
   return z;
2878
638
}
2879
#else
2880
#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
2881
#endif
2882
2883
#if STBTT_RASTERIZER_VERSION == 1
2884
// note: this routine clips fills that extend off the edges... ideally this
2885
// wouldn't happen, but it could happen if the truetype glyph bounding boxes
2886
// are wrong, or if the user supplies a too-small bitmap
2887
static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
2888
{
2889
   // non-zero winding fill
2890
   int x0=0, w=0;
2891
2892
   while (e) {
2893
      if (w == 0) {
2894
         // if we're currently at zero, we need to record the edge start point
2895
         x0 = e->x; w += e->direction;
2896
      } else {
2897
         int x1 = e->x; w += e->direction;
2898
         // if we went to zero, we need to draw
2899
         if (w == 0) {
2900
            int i = x0 >> STBTT_FIXSHIFT;
2901
            int j = x1 >> STBTT_FIXSHIFT;
2902
2903
            if (i < len && j >= 0) {
2904
               if (i == j) {
2905
                  // x0,x1 are the same pixel, so compute combined coverage
2906
                  scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
2907
               } else {
2908
                  if (i >= 0) // add antialiasing for x0
2909
                     scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
2910
                  else
2911
                     i = -1; // clip
2912
2913
                  if (j < len) // add antialiasing for x1
2914
                     scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
2915
                  else
2916
                     j = len; // clip
2917
2918
                  for (++i; i < j; ++i) // fill pixels between x0 and x1
2919
                     scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
2920
               }
2921
            }
2922
         }
2923
      }
2924
2925
      e = e->next;
2926
   }
2927
}
2928
2929
static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
2930
{
2931
   stbtt__hheap hh = { 0, 0, 0 };
2932
   stbtt__active_edge *active = NULL;
2933
   int y,j=0;
2934
   int max_weight = (255 / vsubsample);  // weight per vertical scanline
2935
   int s; // vertical subsample index
2936
   unsigned char scanline_data[512], *scanline;
2937
2938
   if (result->w > 512)
2939
      scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
2940
   else
2941
      scanline = scanline_data;
2942
2943
   y = off_y * vsubsample;
2944
   e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
2945
2946
   while (j < result->h) {
2947
      STBTT_memset(scanline, 0, result->w);
2948
      for (s=0; s < vsubsample; ++s) {
2949
         // find center of pixel for this scanline
2950
         float scan_y = y + 0.5f;
2951
         stbtt__active_edge **step = &active;
2952
2953
         // update all active edges;
2954
         // remove all active edges that terminate before the center of this scanline
2955
         while (*step) {
2956
            stbtt__active_edge * z = *step;
2957
            if (z->ey <= scan_y) {
2958
               *step = z->next; // delete from list
2959
               STBTT_assert(z->direction);
2960
               z->direction = 0;
2961
               stbtt__hheap_free(&hh, z);
2962
            } else {
2963
               z->x += z->dx; // advance to position for current scanline
2964
               step = &((*step)->next); // advance through list
2965
            }
2966
         }
2967
2968
         // resort the list if needed
2969
         for(;;) {
2970
            int changed=0;
2971
            step = &active;
2972
            while (*step && (*step)->next) {
2973
               if ((*step)->x > (*step)->next->x) {
2974
                  stbtt__active_edge *t = *step;
2975
                  stbtt__active_edge *q = t->next;
2976
2977
                  t->next = q->next;
2978
                  q->next = t;
2979
                  *step = q;
2980
                  changed = 1;
2981
               }
2982
               step = &(*step)->next;
2983
            }
2984
            if (!changed) break;
2985
         }
2986
2987
         // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
2988
         while (e->y0 <= scan_y) {
2989
            if (e->y1 > scan_y) {
2990
               stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
2991
               if (z != NULL) {
2992
                  // find insertion point
2993
                  if (active == NULL)
2994
                     active = z;
2995
                  else if (z->x < active->x) {
2996
                     // insert at front
2997
                     z->next = active;
2998
                     active = z;
2999
                  } else {
3000
                     // find thing to insert AFTER
3001
                     stbtt__active_edge *p = active;
3002
                     while (p->next && p->next->x < z->x)
3003
                        p = p->next;
3004
                     // at this point, p->next->x is NOT < z->x
3005
                     z->next = p->next;
3006
                     p->next = z;
3007
                  }
3008
               }
3009
            }
3010
            ++e;
3011
         }
3012
3013
         // now process all active edges in XOR fashion
3014
         if (active)
3015
            stbtt__fill_active_edges(scanline, result->w, active, max_weight);
3016
3017
         ++y;
3018
      }
3019
      STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
3020
      ++j;
3021
   }
3022
3023
   stbtt__hheap_cleanup(&hh, userdata);
3024
3025
   if (scanline != scanline_data)
3026
      STBTT_free(scanline, userdata);
3027
}
3028
3029
#elif STBTT_RASTERIZER_VERSION == 2
3030
3031
// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
3032
// (i.e. it has already been clipped to those)
3033
static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
3034
1.97k
{
3035
1.97k
   if (y0 == y1) return;
3036
1.97k
   STBTT_assert(y0 < y1);
3037
1.97k
   STBTT_assert(e->sy <= e->ey);
3038
1.97k
   if (y0 > e->ey) return;
3039
1.97k
   if (y1 < e->sy) return;
3040
1.97k
   if (y0 < e->sy) {
3041
880
      x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
3042
880
      y0 = e->sy;
3043
880
   }
3044
1.97k
   if (y1 > e->ey) {
3045
0
      x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
3046
0
      y1 = e->ey;
3047
0
   }
3048
3049
1.97k
   if (x0 == x)
3050
986
      STBTT_assert(x1 <= x+1);
3051
986
   else if (x0 == x+1)
3052
0
      STBTT_assert(x1 >= x);
3053
986
   else if (x0 <= x)
3054
986
      STBTT_assert(x1 <= x);
3055
0
   else if (x0 >= x+1)
3056
0
      STBTT_assert(x1 >= x+1);
3057
0
   else
3058
0
      STBTT_assert(x1 >= x && x1 <= x+1);
3059
3060
1.97k
   if (x0 <= x && x1 <= x)
3061
1.97k
      scanline[x] += e->direction * (y1-y0);
3062
0
   else if (x0 >= x+1 && x1 >= x+1)
3063
0
      ;
3064
0
   else {
3065
0
      STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
3066
0
      scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
3067
0
   }
3068
1.97k
}
3069
3070
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
3071
0
{
3072
0
   STBTT_assert(top_width >= 0);
3073
0
   STBTT_assert(bottom_width >= 0);
3074
0
   return (top_width + bottom_width) / 2.0f * height;
3075
0
}
3076
3077
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
3078
0
{
3079
0
   return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
3080
0
}
3081
3082
static float stbtt__sized_triangle_area(float height, float width)
3083
0
{
3084
0
   return height * width / 2;
3085
0
}
3086
3087
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
3088
152
{
3089
152
   float y_bottom = y_top+1;
3090
3091
1.31k
   while (e) {
3092
      // brute force every pixel
3093
3094
      // compute intersection points with top & bottom
3095
1.16k
      STBTT_assert(e->ey >= y_top);
3096
3097
1.16k
      if (e->fdx == 0) {
3098
1.16k
         float x0 = e->fx;
3099
1.16k
         if (x0 < len) {
3100
986
            if (x0 >= 0) {
3101
986
               stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
3102
986
               stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
3103
986
            } else {
3104
0
               stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
3105
0
            }
3106
986
         }
3107
1.16k
      } else {
3108
0
         float x0 = e->fx;
3109
0
         float dx = e->fdx;
3110
0
         float xb = x0 + dx;
3111
0
         float x_top, x_bottom;
3112
0
         float sy0,sy1;
3113
0
         float dy = e->fdy;
3114
0
         STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
3115
3116
         // compute endpoints of line segment clipped to this scanline (if the
3117
         // line segment starts on this scanline. x0 is the intersection of the
3118
         // line with y_top, but that may be off the line segment.
3119
0
         if (e->sy > y_top) {
3120
0
            x_top = x0 + dx * (e->sy - y_top);
3121
0
            sy0 = e->sy;
3122
0
         } else {
3123
0
            x_top = x0;
3124
0
            sy0 = y_top;
3125
0
         }
3126
0
         if (e->ey < y_bottom) {
3127
0
            x_bottom = x0 + dx * (e->ey - y_top);
3128
0
            sy1 = e->ey;
3129
0
         } else {
3130
0
            x_bottom = xb;
3131
0
            sy1 = y_bottom;
3132
0
         }
3133
3134
0
         if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
3135
            // from here on, we don't have to range check x values
3136
3137
0
            if ((int) x_top == (int) x_bottom) {
3138
0
               float height;
3139
               // simple case, only spans one pixel
3140
0
               int x = (int) x_top;
3141
0
               height = (sy1 - sy0) * e->direction;
3142
0
               STBTT_assert(x >= 0 && x < len);
3143
0
               scanline[x]      += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
3144
0
               scanline_fill[x] += height; // everything right of this pixel is filled
3145
0
            } else {
3146
0
               int x,x1,x2;
3147
0
               float y_crossing, y_final, step, sign, area;
3148
               // covers 2+ pixels
3149
0
               if (x_top > x_bottom) {
3150
                  // flip scanline vertically; signed area is the same
3151
0
                  float t;
3152
0
                  sy0 = y_bottom - (sy0 - y_top);
3153
0
                  sy1 = y_bottom - (sy1 - y_top);
3154
0
                  t = sy0, sy0 = sy1, sy1 = t;
3155
0
                  t = x_bottom, x_bottom = x_top, x_top = t;
3156
0
                  dx = -dx;
3157
0
                  dy = -dy;
3158
0
                  t = x0, x0 = xb, xb = t;
3159
0
               }
3160
0
               STBTT_assert(dy >= 0);
3161
0
               STBTT_assert(dx >= 0);
3162
3163
0
               x1 = (int) x_top;
3164
0
               x2 = (int) x_bottom;
3165
               // compute intersection with y axis at x1+1
3166
0
               y_crossing = y_top + dy * (x1+1 - x0);
3167
3168
               // compute intersection with y axis at x2
3169
0
               y_final = y_top + dy * (x2 - x0);
3170
3171
               //           x1    x_top                            x2    x_bottom
3172
               //     y_top  +------|-----+------------+------------+--------|---+------------+
3173
               //            |            |            |            |            |            |
3174
               //            |            |            |            |            |            |
3175
               //       sy0  |      Txxxxx|............|............|............|............|
3176
               // y_crossing |            *xxxxx.......|............|............|............|
3177
               //            |            |     xxxxx..|............|............|............|
3178
               //            |            |     /-   xx*xxxx........|............|............|
3179
               //            |            | dy <       |    xxxxxx..|............|............|
3180
               //   y_final  |            |     \-     |          xx*xxx.........|............|
3181
               //       sy1  |            |            |            |   xxxxxB...|............|
3182
               //            |            |            |            |            |            |
3183
               //            |            |            |            |            |            |
3184
               //  y_bottom  +------------+------------+------------+------------+------------+
3185
               //
3186
               // goal is to measure the area covered by '.' in each pixel
3187
3188
               // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
3189
               // @TODO: maybe test against sy1 rather than y_bottom?
3190
0
               if (y_crossing > y_bottom)
3191
0
                  y_crossing = y_bottom;
3192
3193
0
               sign = e->direction;
3194
3195
               // area of the rectangle covered from sy0..y_crossing
3196
0
               area = sign * (y_crossing-sy0);
3197
3198
               // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
3199
0
               scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
3200
3201
               // check if final y_crossing is blown up; no test case for this
3202
0
               if (y_final > y_bottom) {
3203
0
                  int denom = (x2 - (x1+1));
3204
0
                  y_final = y_bottom;
3205
0
                  if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316)
3206
0
                     dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom
3207
0
                  }
3208
0
               }
3209
3210
               // in second pixel, area covered by line segment found in first pixel
3211
               // is always a rectangle 1 wide * the height of that line segment; this
3212
               // is exactly what the variable 'area' stores. it also gets a contribution
3213
               // from the line segment within it. the THIRD pixel will get the first
3214
               // pixel's rectangle contribution, the second pixel's rectangle contribution,
3215
               // and its own contribution. the 'own contribution' is the same in every pixel except
3216
               // the leftmost and rightmost, a trapezoid that slides down in each pixel.
3217
               // the second pixel's contribution to the third pixel will be the
3218
               // rectangle 1 wide times the height change in the second pixel, which is dy.
3219
3220
0
               step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
3221
               // which multiplied by 1-pixel-width is how much pixel area changes for each step in x
3222
               // so the area advances by 'step' every time
3223
3224
0
               for (x = x1+1; x < x2; ++x) {
3225
0
                  scanline[x] += area + step/2; // area of trapezoid is 1*step/2
3226
0
                  area += step;
3227
0
               }
3228
0
               STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
3229
0
               STBTT_assert(sy1 > y_final-0.01f);
3230
3231
               // area covered in the last pixel is the rectangle from all the pixels to the left,
3232
               // plus the trapezoid filled by the line segment in this pixel all the way to the right edge
3233
0
               scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
3234
3235
               // the rest of the line is filled based on the total height of the line segment in this pixel
3236
0
               scanline_fill[x2] += sign * (sy1-sy0);
3237
0
            }
3238
0
         } else {
3239
            // if edge goes outside of box we're drawing, we require
3240
            // clipping logic. since this does not match the intended use
3241
            // of this library, we use a different, very slow brute
3242
            // force implementation
3243
            // note though that this does happen some of the time because
3244
            // x_top and x_bottom can be extrapolated at the top & bottom of
3245
            // the shape and actually lie outside the bounding box
3246
0
            int x;
3247
0
            for (x=0; x < len; ++x) {
3248
               // cases:
3249
               //
3250
               // there can be up to two intersections with the pixel. any intersection
3251
               // with left or right edges can be handled by splitting into two (or three)
3252
               // regions. intersections with top & bottom do not necessitate case-wise logic.
3253
               //
3254
               // the old way of doing this found the intersections with the left & right edges,
3255
               // then used some simple logic to produce up to three segments in sorted order
3256
               // from top-to-bottom. however, this had a problem: if an x edge was epsilon
3257
               // across the x border, then the corresponding y position might not be distinct
3258
               // from the other y segment, and it might ignored as an empty segment. to avoid
3259
               // that, we need to explicitly produce segments based on x positions.
3260
3261
               // rename variables to clearly-defined pairs
3262
0
               float y0 = y_top;
3263
0
               float x1 = (float) (x);
3264
0
               float x2 = (float) (x+1);
3265
0
               float x3 = xb;
3266
0
               float y3 = y_bottom;
3267
3268
               // x = e->x + e->dx * (y-y_top)
3269
               // (y-y_top) = (x - e->x) / e->dx
3270
               // y = (x - e->x) / e->dx + y_top
3271
0
               float y1 = (x - x0) / dx + y_top;
3272
0
               float y2 = (x+1 - x0) / dx + y_top;
3273
3274
0
               if (x0 < x1 && x3 > x2) {         // three segments descending down-right
3275
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
3276
0
                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
3277
0
                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
3278
0
               } else if (x3 < x1 && x0 > x2) {  // three segments descending down-left
3279
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
3280
0
                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
3281
0
                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
3282
0
               } else if (x0 < x1 && x3 > x1) {  // two segments across x, down-right
3283
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
3284
0
                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
3285
0
               } else if (x3 < x1 && x0 > x1) {  // two segments across x, down-left
3286
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
3287
0
                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
3288
0
               } else if (x0 < x2 && x3 > x2) {  // two segments across x+1, down-right
3289
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
3290
0
                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
3291
0
               } else if (x3 < x2 && x0 > x2) {  // two segments across x+1, down-left
3292
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
3293
0
                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
3294
0
               } else {  // one segment
3295
0
                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
3296
0
               }
3297
0
            }
3298
0
         }
3299
0
      }
3300
1.16k
      e = e->next;
3301
1.16k
   }
3302
152
}
3303
3304
// directly AA rasterize edges w/o supersampling
3305
static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
3306
20
{
3307
20
   stbtt__hheap hh = { 0, 0, 0 };
3308
20
   stbtt__active_edge *active = NULL;
3309
20
   int y,j=0, i;
3310
20
   float scanline_data[129], *scanline, *scanline2;
3311
3312
20
   STBTT__NOTUSED(vsubsample);
3313
3314
20
   if (result->w > 64)
3315
0
      scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
3316
20
   else
3317
20
      scanline = scanline_data;
3318
3319
20
   scanline2 = scanline + result->w;
3320
3321
20
   y = off_y;
3322
20
   e[n].y0 = (float) (off_y + result->h) + 1;
3323
3324
173
   while (j < result->h) {
3325
      // find center of pixel for this scanline
3326
153
      float scan_y_top    = y + 0.0f;
3327
153
      float scan_y_bottom = y + 1.0f;
3328
153
      stbtt__active_edge **step = &active;
3329
3330
153
      STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
3331
153
      STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
3332
3333
      // update all active edges;
3334
      // remove all active edges that terminate before the top of this scanline
3335
1.21k
      while (*step) {
3336
1.06k
         stbtt__active_edge * z = *step;
3337
1.06k
         if (z->ey <= scan_y_top) {
3338
538
            *step = z->next; // delete from list
3339
538
            STBTT_assert(z->direction);
3340
538
            z->direction = 0;
3341
538
            stbtt__hheap_free(&hh, z);
3342
538
         } else {
3343
522
            step = &((*step)->next); // advance through list
3344
522
         }
3345
1.06k
      }
3346
3347
      // insert all edges that start before the bottom of this scanline
3348
791
      while (e->y0 <= scan_y_bottom) {
3349
638
         if (e->y0 != e->y1) {
3350
638
            stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
3351
638
            if (z != NULL) {
3352
638
               if (j == 0 && off_y != 0) {
3353
184
                  if (z->ey < scan_y_top) {
3354
                     // this can happen due to subpixel positioning and some kind of fp rounding error i think
3355
0
                     z->ey = scan_y_top;
3356
0
                  }
3357
184
               }
3358
638
               STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds
3359
               // insert at front
3360
638
               z->next = active;
3361
638
               active = z;
3362
638
            }
3363
638
         }
3364
638
         ++e;
3365
638
      }
3366
3367
      // now process all active edges
3368
153
      if (active)
3369
152
         stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
3370
3371
153
      {
3372
153
         float sum = 0;
3373
916
         for (i=0; i < result->w; ++i) {
3374
763
            float k;
3375
763
            int m;
3376
763
            sum += scanline2[i];
3377
763
            k = scanline[i] + sum;
3378
763
            k = (float) STBTT_fabs(k)*255 + 0.5f;
3379
763
            m = (int) k;
3380
763
            if (m > 255) m = 255;
3381
763
            result->pixels[j*result->stride + i] = (unsigned char) m;
3382
763
         }
3383
153
      }
3384
      // advance all the edges
3385
153
      step = &active;
3386
1.31k
      while (*step) {
3387
1.16k
         stbtt__active_edge *z = *step;
3388
1.16k
         z->fx += z->fdx; // advance to position for current scanline
3389
1.16k
         step = &((*step)->next); // advance through list
3390
1.16k
      }
3391
3392
153
      ++y;
3393
153
      ++j;
3394
153
   }
3395
3396
20
   stbtt__hheap_cleanup(&hh, userdata);
3397
3398
20
   if (scanline != scanline_data)
3399
0
      STBTT_free(scanline, userdata);
3400
20
}
3401
#else
3402
#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
3403
#endif
3404
3405
2.60k
#define STBTT__COMPARE(a,b)  ((a)->y0 < (b)->y0)
3406
3407
static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
3408
20
{
3409
20
   int i,j;
3410
638
   for (i=1; i < n; ++i) {
3411
618
      stbtt__edge t = p[i], *a = &t;
3412
618
      j = i;
3413
1.10k
      while (j > 0) {
3414
1.08k
         stbtt__edge *b = &p[j-1];
3415
1.08k
         int c = STBTT__COMPARE(a,b);
3416
1.08k
         if (!c) break;
3417
488
         p[j] = p[j-1];
3418
488
         --j;
3419
488
      }
3420
618
      if (i != j)
3421
301
         p[j] = t;
3422
618
   }
3423
20
}
3424
3425
static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
3426
81
{
3427
   /* threshold for transitioning to insertion sort */
3428
142
   while (n > 12) {
3429
61
      stbtt__edge t;
3430
61
      int c01,c12,c,m,i,j;
3431
3432
      /* compute median of three */
3433
61
      m = n >> 1;
3434
61
      c01 = STBTT__COMPARE(&p[0],&p[m]);
3435
61
      c12 = STBTT__COMPARE(&p[m],&p[n-1]);
3436
      /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
3437
61
      if (c01 != c12) {
3438
         /* otherwise, we'll need to swap something else to middle */
3439
17
         int z;
3440
17
         c = STBTT__COMPARE(&p[0],&p[n-1]);
3441
         /* 0>mid && mid<n:  0>n => n; 0<n => 0 */
3442
         /* 0<mid && mid>n:  0>n => 0; 0<n => n */
3443
17
         z = (c == c12) ? 0 : n-1;
3444
17
         t = p[z];
3445
17
         p[z] = p[m];
3446
17
         p[m] = t;
3447
17
      }
3448
      /* now p[m] is the median-of-three */
3449
      /* swap it to the beginning so it won't move around */
3450
61
      t = p[0];
3451
61
      p[0] = p[m];
3452
61
      p[m] = t;
3453
3454
      /* partition loop */
3455
61
      i=1;
3456
61
      j=n-1;
3457
181
      for(;;) {
3458
         /* handling of equality is crucial here */
3459
         /* for sentinels & efficiency with duplicates */
3460
804
         for (;;++i) {
3461
804
            if (!STBTT__COMPARE(&p[i], &p[0])) break;
3462
804
         }
3463
572
         for (;;--j) {
3464
572
            if (!STBTT__COMPARE(&p[0], &p[j])) break;
3465
572
         }
3466
         /* make sure we haven't crossed */
3467
181
         if (i >= j) break;
3468
120
         t = p[i];
3469
120
         p[i] = p[j];
3470
120
         p[j] = t;
3471
3472
120
         ++i;
3473
120
         --j;
3474
120
      }
3475
      /* recurse on smaller side, iterate on larger */
3476
61
      if (j < (n-i)) {
3477
18
         stbtt__sort_edges_quicksort(p,j);
3478
18
         p = p+i;
3479
18
         n = n-i;
3480
43
      } else {
3481
43
         stbtt__sort_edges_quicksort(p+i, n-i);
3482
43
         n = j;
3483
43
      }
3484
61
   }
3485
81
}
3486
3487
static void stbtt__sort_edges(stbtt__edge *p, int n)
3488
20
{
3489
20
   stbtt__sort_edges_quicksort(p, n);
3490
20
   stbtt__sort_edges_ins_sort(p, n);
3491
20
}
3492
3493
typedef struct
3494
{
3495
   float x,y;
3496
} stbtt__point;
3497
3498
static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
3499
20
{
3500
20
   float y_scale_inv = invert ? -scale_y : scale_y;
3501
20
   stbtt__edge *e;
3502
20
   int n,i,j,k,m;
3503
#if STBTT_RASTERIZER_VERSION == 1
3504
   int vsubsample = result->h < 8 ? 15 : 5;
3505
#elif STBTT_RASTERIZER_VERSION == 2
3506
   int vsubsample = 1;
3507
#else
3508
   #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
3509
#endif
3510
   // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
3511
3512
   // now we have to blow out the windings into explicit edge lists
3513
20
   n = 0;
3514
339
   for (i=0; i < windings; ++i)
3515
319
      n += wcount[i];
3516
3517
20
   e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
3518
20
   if (e == 0) return;
3519
20
   n = 0;
3520
3521
20
   m=0;
3522
339
   for (i=0; i < windings; ++i) {
3523
319
      stbtt__point *p = pts + m;
3524
319
      m += wcount[i];
3525
319
      j = wcount[i]-1;
3526
1.91k
      for (k=0; k < wcount[i]; j=k++) {
3527
1.59k
         int a=k,b=j;
3528
         // skip the edge if horizontal
3529
1.59k
         if (p[j].y == p[k].y)
3530
957
            continue;
3531
         // add edge from j to k to the list
3532
638
         e[n].invert = 0;
3533
638
         if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
3534
319
            e[n].invert = 1;
3535
319
            a=j,b=k;
3536
319
         }
3537
638
         e[n].x0 = p[a].x * scale_x + shift_x;
3538
638
         e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
3539
638
         e[n].x1 = p[b].x * scale_x + shift_x;
3540
638
         e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
3541
638
         ++n;
3542
638
      }
3543
319
   }
3544
3545
   // now sort the edges by their highest point (should snap to integer, and then by x)
3546
   //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
3547
20
   stbtt__sort_edges(e, n);
3548
3549
   // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
3550
20
   stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
3551
3552
20
   STBTT_free(e, userdata);
3553
20
}
3554
3555
static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
3556
3.19k
{
3557
3.19k
   if (!points) return; // during first pass, it's unallocated
3558
1.59k
   points[n].x = x;
3559
1.59k
   points[n].y = y;
3560
1.59k
}
3561
3562
// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
3563
static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
3564
0
{
3565
   // midpoint
3566
0
   float mx = (x0 + 2*x1 + x2)/4;
3567
0
   float my = (y0 + 2*y1 + y2)/4;
3568
   // versus directly drawn line
3569
0
   float dx = (x0+x2)/2 - mx;
3570
0
   float dy = (y0+y2)/2 - my;
3571
0
   if (n > 16) // 65536 segments on one curve better be enough!
3572
0
      return 1;
3573
0
   if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
3574
0
      stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
3575
0
      stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
3576
0
   } else {
3577
0
      stbtt__add_point(points, *num_points,x2,y2);
3578
0
      *num_points = *num_points+1;
3579
0
   }
3580
0
   return 1;
3581
0
}
3582
3583
static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
3584
0
{
3585
   // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
3586
0
   float dx0 = x1-x0;
3587
0
   float dy0 = y1-y0;
3588
0
   float dx1 = x2-x1;
3589
0
   float dy1 = y2-y1;
3590
0
   float dx2 = x3-x2;
3591
0
   float dy2 = y3-y2;
3592
0
   float dx = x3-x0;
3593
0
   float dy = y3-y0;
3594
0
   float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));
3595
0
   float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);
3596
0
   float flatness_squared = longlen*longlen-shortlen*shortlen;
3597
3598
0
   if (n > 16) // 65536 segments on one curve better be enough!
3599
0
      return;
3600
3601
0
   if (flatness_squared > objspace_flatness_squared) {
3602
0
      float x01 = (x0+x1)/2;
3603
0
      float y01 = (y0+y1)/2;
3604
0
      float x12 = (x1+x2)/2;
3605
0
      float y12 = (y1+y2)/2;
3606
0
      float x23 = (x2+x3)/2;
3607
0
      float y23 = (y2+y3)/2;
3608
3609
0
      float xa = (x01+x12)/2;
3610
0
      float ya = (y01+y12)/2;
3611
0
      float xb = (x12+x23)/2;
3612
0
      float yb = (y12+y23)/2;
3613
3614
0
      float mx = (xa+xb)/2;
3615
0
      float my = (ya+yb)/2;
3616
3617
0
      stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
3618
0
      stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
3619
0
   } else {
3620
0
      stbtt__add_point(points, *num_points,x3,y3);
3621
0
      *num_points = *num_points+1;
3622
0
   }
3623
0
}
3624
3625
// returns number of contours
3626
static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
3627
20
{
3628
20
   stbtt__point *points=0;
3629
20
   int num_points=0;
3630
3631
20
   float objspace_flatness_squared = objspace_flatness * objspace_flatness;
3632
20
   int i,n=0,start=0, pass;
3633
3634
   // count how many "moves" there are to get the contour count
3635
1.61k
   for (i=0; i < num_verts; ++i)
3636
1.59k
      if (vertices[i].type == STBTT_vmove)
3637
319
         ++n;
3638
3639
20
   *num_contours = n;
3640
20
   if (n == 0) return 0;
3641
3642
20
   *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
3643
3644
20
   if (*contour_lengths == 0) {
3645
0
      *num_contours = 0;
3646
0
      return 0;
3647
0
   }
3648
3649
   // make two passes through the points so we don't need to realloc
3650
60
   for (pass=0; pass < 2; ++pass) {
3651
40
      float x=0,y=0;
3652
40
      if (pass == 1) {
3653
20
         points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
3654
20
         if (points == NULL) goto error;
3655
20
      }
3656
40
      num_points = 0;
3657
40
      n= -1;
3658
3.23k
      for (i=0; i < num_verts; ++i) {
3659
3.19k
         switch (vertices[i].type) {
3660
638
            case STBTT_vmove:
3661
               // start the next contour
3662
638
               if (n >= 0)
3663
598
                  (*contour_lengths)[n] = num_points - start;
3664
638
               ++n;
3665
638
               start = num_points;
3666
3667
638
               x = vertices[i].x, y = vertices[i].y;
3668
638
               stbtt__add_point(points, num_points++, x,y);
3669
638
               break;
3670
2.55k
            case STBTT_vline:
3671
2.55k
               x = vertices[i].x, y = vertices[i].y;
3672
2.55k
               stbtt__add_point(points, num_points++, x, y);
3673
2.55k
               break;
3674
0
            case STBTT_vcurve:
3675
0
               stbtt__tesselate_curve(points, &num_points, x,y,
3676
0
                                        vertices[i].cx, vertices[i].cy,
3677
0
                                        vertices[i].x,  vertices[i].y,
3678
0
                                        objspace_flatness_squared, 0);
3679
0
               x = vertices[i].x, y = vertices[i].y;
3680
0
               break;
3681
0
            case STBTT_vcubic:
3682
0
               stbtt__tesselate_cubic(points, &num_points, x,y,
3683
0
                                        vertices[i].cx, vertices[i].cy,
3684
0
                                        vertices[i].cx1, vertices[i].cy1,
3685
0
                                        vertices[i].x,  vertices[i].y,
3686
0
                                        objspace_flatness_squared, 0);
3687
0
               x = vertices[i].x, y = vertices[i].y;
3688
0
               break;
3689
3.19k
         }
3690
3.19k
      }
3691
40
      (*contour_lengths)[n] = num_points - start;
3692
40
   }
3693
3694
20
   return points;
3695
0
error:
3696
0
   STBTT_free(points, userdata);
3697
0
   STBTT_free(*contour_lengths, userdata);
3698
0
   *contour_lengths = 0;
3699
0
   *num_contours = 0;
3700
0
   return NULL;
3701
20
}
3702
3703
STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
3704
20
{
3705
20
   float scale            = scale_x > scale_y ? scale_y : scale_x;
3706
20
   int winding_count      = 0;
3707
20
   int *winding_lengths   = NULL;
3708
20
   stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
3709
20
   if (windings) {
3710
20
      stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
3711
20
      STBTT_free(winding_lengths, userdata);
3712
20
      STBTT_free(windings, userdata);
3713
20
   }
3714
20
}
3715
3716
STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
3717
0
{
3718
0
   STBTT_free(bitmap, userdata);
3719
0
}
3720
3721
STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
3722
0
{
3723
0
   int ix0,iy0,ix1,iy1;
3724
0
   stbtt__bitmap gbm;
3725
0
   stbtt_vertex *vertices;
3726
0
   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
3727
0
3728
0
   if (scale_x == 0) scale_x = scale_y;
3729
0
   if (scale_y == 0) {
3730
0
      if (scale_x == 0) {
3731
0
         STBTT_free(vertices, info->userdata);
3732
0
         return NULL;
3733
0
      }
3734
0
      scale_y = scale_x;
3735
0
   }
3736
0
3737
0
   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
3738
0
3739
0
   // now we get the size
3740
0
   gbm.w = (ix1 - ix0);
3741
0
   gbm.h = (iy1 - iy0);
3742
0
   gbm.pixels = NULL; // in case we error
3743
0
3744
0
   if (width ) *width  = gbm.w;
3745
0
   if (height) *height = gbm.h;
3746
0
   if (xoff  ) *xoff   = ix0;
3747
0
   if (yoff  ) *yoff   = iy0;
3748
0
3749
0
   if (gbm.w && gbm.h) {
3750
0
      gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
3751
0
      if (gbm.pixels) {
3752
0
         gbm.stride = gbm.w;
3753
0
3754
0
         stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
3755
0
      }
3756
0
   }
3757
0
   STBTT_free(vertices, info->userdata);
3758
0
   return gbm.pixels;
3759
0
}
3760
3761
STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
3762
0
{
3763
0
   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
3764
0
}
3765
3766
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
3767
20
{
3768
20
   int ix0,iy0;
3769
20
   stbtt_vertex *vertices;
3770
20
   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
3771
20
   stbtt__bitmap gbm;
3772
3773
20
   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
3774
20
   gbm.pixels = output;
3775
20
   gbm.w = out_w;
3776
20
   gbm.h = out_h;
3777
20
   gbm.stride = out_stride;
3778
3779
20
   if (gbm.w && gbm.h)
3780
20
      stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
3781
3782
20
   STBTT_free(vertices, info->userdata);
3783
20
}
3784
3785
STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
3786
0
{
3787
0
   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
3788
0
}
3789
3790
STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
3791
0
{
3792
0
   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
3793
0
}
3794
3795
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)
3796
0
{
3797
0
   stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));
3798
0
}
3799
3800
STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
3801
0
{
3802
0
   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
3803
0
}
3804
3805
STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
3806
0
{
3807
0
   return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
3808
0
}
3809
3810
STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
3811
0
{
3812
0
   stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
3813
0
}
3814
3815
//////////////////////////////////////////////////////////////////////////////
3816
//
3817
// bitmap baking
3818
//
3819
// This is SUPER-CRAPPY packing to keep source code small
3820
3821
static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)
3822
                                float pixel_height,                     // height of font in pixels
3823
                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in
3824
                                int first_char, int num_chars,          // characters to bake
3825
                                stbtt_bakedchar *chardata)
3826
0
{
3827
0
   float scale;
3828
0
   int x,y,bottom_y, i;
3829
0
   stbtt_fontinfo f;
3830
0
   f.userdata = NULL;
3831
0
   if (!stbtt_InitFont(&f, data, offset))
3832
0
      return -1;
3833
0
   STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
3834
0
   x=y=1;
3835
0
   bottom_y = 1;
3836
0
3837
0
   scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
3838
0
3839
0
   for (i=0; i < num_chars; ++i) {
3840
0
      int advance, lsb, x0,y0,x1,y1,gw,gh;
3841
0
      int g = stbtt_FindGlyphIndex(&f, first_char + i);
3842
0
      stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
3843
0
      stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
3844
0
      gw = x1-x0;
3845
0
      gh = y1-y0;
3846
0
      if (x + gw + 1 >= pw)
3847
0
         y = bottom_y, x = 1; // advance to next row
3848
0
      if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
3849
0
         return -i;
3850
0
      STBTT_assert(x+gw < pw);
3851
0
      STBTT_assert(y+gh < ph);
3852
0
      stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
3853
0
      chardata[i].x0 = (stbtt_int16) x;
3854
0
      chardata[i].y0 = (stbtt_int16) y;
3855
0
      chardata[i].x1 = (stbtt_int16) (x + gw);
3856
0
      chardata[i].y1 = (stbtt_int16) (y + gh);
3857
0
      chardata[i].xadvance = scale * advance;
3858
0
      chardata[i].xoff     = (float) x0;
3859
0
      chardata[i].yoff     = (float) y0;
3860
0
      x = x + gw + 1;
3861
0
      if (y+gh+1 > bottom_y)
3862
0
         bottom_y = y+gh+1;
3863
0
   }
3864
0
   return bottom_y;
3865
0
}
3866
3867
STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
3868
0
{
3869
0
   float d3d_bias = opengl_fillrule ? 0 : -0.5f;
3870
0
   float ipw = 1.0f / pw, iph = 1.0f / ph;
3871
0
   const stbtt_bakedchar *b = chardata + char_index;
3872
0
   int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
3873
0
   int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
3874
0
3875
0
   q->x0 = round_x + d3d_bias;
3876
0
   q->y0 = round_y + d3d_bias;
3877
0
   q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
3878
0
   q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
3879
0
3880
0
   q->s0 = b->x0 * ipw;
3881
0
   q->t0 = b->y0 * iph;
3882
0
   q->s1 = b->x1 * ipw;
3883
0
   q->t1 = b->y1 * iph;
3884
0
3885
0
   *xpos += b->xadvance;
3886
0
}
3887
3888
//////////////////////////////////////////////////////////////////////////////
3889
//
3890
// rectangle packing replacement routines if you don't have stb_rect_pack.h
3891
//
3892
3893
#ifndef STB_RECT_PACK_VERSION
3894
3895
typedef int stbrp_coord;
3896
3897
////////////////////////////////////////////////////////////////////////////////////
3898
//                                                                                //
3899
//                                                                                //
3900
// COMPILER WARNING ?!?!?                                                         //
3901
//                                                                                //
3902
//                                                                                //
3903
// if you get a compile warning due to these symbols being defined more than      //
3904
// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h"         //
3905
//                                                                                //
3906
////////////////////////////////////////////////////////////////////////////////////
3907
3908
typedef struct
3909
{
3910
   int width,height;
3911
   int x,y,bottom_y;
3912
} stbrp_context;
3913
3914
typedef struct
3915
{
3916
   unsigned char x;
3917
} stbrp_node;
3918
3919
struct stbrp_rect
3920
{
3921
   stbrp_coord x,y;
3922
   int id,w,h,was_packed;
3923
};
3924
3925
static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
3926
{
3927
   con->width  = pw;
3928
   con->height = ph;
3929
   con->x = 0;
3930
   con->y = 0;
3931
   con->bottom_y = 0;
3932
   STBTT__NOTUSED(nodes);
3933
   STBTT__NOTUSED(num_nodes);
3934
}
3935
3936
static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
3937
{
3938
   int i;
3939
   for (i=0; i < num_rects; ++i) {
3940
      if (con->x + rects[i].w > con->width) {
3941
         con->x = 0;
3942
         con->y = con->bottom_y;
3943
      }
3944
      if (con->y + rects[i].h > con->height)
3945
         break;
3946
      rects[i].x = con->x;
3947
      rects[i].y = con->y;
3948
      rects[i].was_packed = 1;
3949
      con->x += rects[i].w;
3950
      if (con->y + rects[i].h > con->bottom_y)
3951
         con->bottom_y = con->y + rects[i].h;
3952
   }
3953
   for (   ; i < num_rects; ++i)
3954
      rects[i].was_packed = 0;
3955
}
3956
#endif
3957
3958
//////////////////////////////////////////////////////////////////////////////
3959
//
3960
// bitmap baking
3961
//
3962
// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
3963
// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
3964
3965
STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
3966
0
{
3967
0
   stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);
3968
0
   int            num_nodes = pw - padding;
3969
0
   stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);
3970
0
3971
0
   if (context == NULL || nodes == NULL) {
3972
0
      if (context != NULL) STBTT_free(context, alloc_context);
3973
0
      if (nodes   != NULL) STBTT_free(nodes  , alloc_context);
3974
0
      return 0;
3975
0
   }
3976
0
3977
0
   spc->user_allocator_context = alloc_context;
3978
0
   spc->width = pw;
3979
0
   spc->height = ph;
3980
0
   spc->pixels = pixels;
3981
0
   spc->pack_info = context;
3982
0
   spc->nodes = nodes;
3983
0
   spc->padding = padding;
3984
0
   spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
3985
0
   spc->h_oversample = 1;
3986
0
   spc->v_oversample = 1;
3987
0
   spc->skip_missing = 0;
3988
0
3989
0
   stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
3990
0
3991
0
   if (pixels)
3992
0
      STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
3993
0
3994
0
   return 1;
3995
0
}
3996
3997
STBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc)
3998
0
{
3999
0
   STBTT_free(spc->nodes    , spc->user_allocator_context);
4000
0
   STBTT_free(spc->pack_info, spc->user_allocator_context);
4001
0
}
4002
4003
STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
4004
0
{
4005
0
   STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
4006
0
   STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
4007
0
   if (h_oversample <= STBTT_MAX_OVERSAMPLE)
4008
0
      spc->h_oversample = h_oversample;
4009
0
   if (v_oversample <= STBTT_MAX_OVERSAMPLE)
4010
0
      spc->v_oversample = v_oversample;
4011
0
}
4012
4013
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)
4014
0
{
4015
0
   spc->skip_missing = skip;
4016
0
}
4017
4018
0
#define STBTT__OVER_MASK  (STBTT_MAX_OVERSAMPLE-1)
4019
4020
static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
4021
0
{
4022
0
   unsigned char buffer[STBTT_MAX_OVERSAMPLE];
4023
0
   int safe_w = w - kernel_width;
4024
0
   int j;
4025
0
   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
4026
0
   for (j=0; j < h; ++j) {
4027
0
      int i;
4028
0
      unsigned int total;
4029
0
      STBTT_memset(buffer, 0, kernel_width);
4030
4031
0
      total = 0;
4032
4033
      // make kernel_width a constant in common cases so compiler can optimize out the divide
4034
0
      switch (kernel_width) {
4035
0
         case 2:
4036
0
            for (i=0; i <= safe_w; ++i) {
4037
0
               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
4038
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
4039
0
               pixels[i] = (unsigned char) (total / 2);
4040
0
            }
4041
0
            break;
4042
0
         case 3:
4043
0
            for (i=0; i <= safe_w; ++i) {
4044
0
               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
4045
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
4046
0
               pixels[i] = (unsigned char) (total / 3);
4047
0
            }
4048
0
            break;
4049
0
         case 4:
4050
0
            for (i=0; i <= safe_w; ++i) {
4051
0
               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
4052
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
4053
0
               pixels[i] = (unsigned char) (total / 4);
4054
0
            }
4055
0
            break;
4056
0
         case 5:
4057
0
            for (i=0; i <= safe_w; ++i) {
4058
0
               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
4059
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
4060
0
               pixels[i] = (unsigned char) (total / 5);
4061
0
            }
4062
0
            break;
4063
0
         default:
4064
0
            for (i=0; i <= safe_w; ++i) {
4065
0
               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
4066
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
4067
0
               pixels[i] = (unsigned char) (total / kernel_width);
4068
0
            }
4069
0
            break;
4070
0
      }
4071
4072
0
      for (; i < w; ++i) {
4073
0
         STBTT_assert(pixels[i] == 0);
4074
0
         total -= buffer[i & STBTT__OVER_MASK];
4075
0
         pixels[i] = (unsigned char) (total / kernel_width);
4076
0
      }
4077
4078
0
      pixels += stride_in_bytes;
4079
0
   }
4080
0
}
4081
4082
static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
4083
0
{
4084
0
   unsigned char buffer[STBTT_MAX_OVERSAMPLE];
4085
0
   int safe_h = h - kernel_width;
4086
0
   int j;
4087
0
   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
4088
0
   for (j=0; j < w; ++j) {
4089
0
      int i;
4090
0
      unsigned int total;
4091
0
      STBTT_memset(buffer, 0, kernel_width);
4092
4093
0
      total = 0;
4094
4095
      // make kernel_width a constant in common cases so compiler can optimize out the divide
4096
0
      switch (kernel_width) {
4097
0
         case 2:
4098
0
            for (i=0; i <= safe_h; ++i) {
4099
0
               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4100
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4101
0
               pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
4102
0
            }
4103
0
            break;
4104
0
         case 3:
4105
0
            for (i=0; i <= safe_h; ++i) {
4106
0
               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4107
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4108
0
               pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
4109
0
            }
4110
0
            break;
4111
0
         case 4:
4112
0
            for (i=0; i <= safe_h; ++i) {
4113
0
               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4114
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4115
0
               pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
4116
0
            }
4117
0
            break;
4118
0
         case 5:
4119
0
            for (i=0; i <= safe_h; ++i) {
4120
0
               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4121
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4122
0
               pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
4123
0
            }
4124
0
            break;
4125
0
         default:
4126
0
            for (i=0; i <= safe_h; ++i) {
4127
0
               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4128
0
               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4129
0
               pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
4130
0
            }
4131
0
            break;
4132
0
      }
4133
4134
0
      for (; i < h; ++i) {
4135
0
         STBTT_assert(pixels[i*stride_in_bytes] == 0);
4136
0
         total -= buffer[i & STBTT__OVER_MASK];
4137
0
         pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
4138
0
      }
4139
4140
0
      pixels += 1;
4141
0
   }
4142
0
}
4143
4144
static float stbtt__oversample_shift(int oversample)
4145
40
{
4146
40
   if (!oversample)
4147
0
      return 0.0f;
4148
4149
   // The prefilter is a box filter of width "oversample",
4150
   // which shifts phase by (oversample - 1)/2 pixels in
4151
   // oversampled space. We want to shift in the opposite
4152
   // direction to counter this.
4153
40
   return (float)-(oversample - 1) / (2.0f * (float)oversample);
4154
40
}
4155
4156
// rects array must be big enough to accommodate all characters in the given ranges
4157
STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
4158
0
{
4159
0
   int i,j,k;
4160
0
   int missing_glyph_added = 0;
4161
0
4162
0
   k=0;
4163
0
   for (i=0; i < num_ranges; ++i) {
4164
0
      float fh = ranges[i].font_size;
4165
0
      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
4166
0
      ranges[i].h_oversample = (unsigned char) spc->h_oversample;
4167
0
      ranges[i].v_oversample = (unsigned char) spc->v_oversample;
4168
0
      for (j=0; j < ranges[i].num_chars; ++j) {
4169
0
         int x0,y0,x1,y1;
4170
0
         int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
4171
0
         int glyph = stbtt_FindGlyphIndex(info, codepoint);
4172
0
         if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {
4173
0
            rects[k].w = rects[k].h = 0;
4174
0
         } else {
4175
0
            stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
4176
0
                                            scale * spc->h_oversample,
4177
0
                                            scale * spc->v_oversample,
4178
0
                                            0,0,
4179
0
                                            &x0,&y0,&x1,&y1);
4180
0
            rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
4181
0
            rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
4182
0
            if (glyph == 0)
4183
0
               missing_glyph_added = 1;
4184
0
         }
4185
0
         ++k;
4186
0
      }
4187
0
   }
4188
0
4189
0
   return k;
4190
0
}
4191
4192
STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)
4193
20
{
4194
20
   stbtt_MakeGlyphBitmapSubpixel(info,
4195
20
                                 output,
4196
20
                                 out_w - (prefilter_x - 1),
4197
20
                                 out_h - (prefilter_y - 1),
4198
20
                                 out_stride,
4199
20
                                 scale_x,
4200
20
                                 scale_y,
4201
20
                                 shift_x,
4202
20
                                 shift_y,
4203
20
                                 glyph);
4204
4205
20
   if (prefilter_x > 1)
4206
0
      stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);
4207
4208
20
   if (prefilter_y > 1)
4209
0
      stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);
4210
4211
20
   *sub_x = stbtt__oversample_shift(prefilter_x);
4212
20
   *sub_y = stbtt__oversample_shift(prefilter_y);
4213
20
}
4214
4215
// rects array must be big enough to accommodate all characters in the given ranges
4216
STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
4217
0
{
4218
0
   int i,j,k, missing_glyph = -1, return_value = 1;
4219
0
4220
0
   // save current values
4221
0
   int old_h_over = spc->h_oversample;
4222
0
   int old_v_over = spc->v_oversample;
4223
0
4224
0
   k = 0;
4225
0
   for (i=0; i < num_ranges; ++i) {
4226
0
      float fh = ranges[i].font_size;
4227
0
      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
4228
0
      float recip_h,recip_v,sub_x,sub_y;
4229
0
      spc->h_oversample = ranges[i].h_oversample;
4230
0
      spc->v_oversample = ranges[i].v_oversample;
4231
0
      recip_h = 1.0f / spc->h_oversample;
4232
0
      recip_v = 1.0f / spc->v_oversample;
4233
0
      sub_x = stbtt__oversample_shift(spc->h_oversample);
4234
0
      sub_y = stbtt__oversample_shift(spc->v_oversample);
4235
0
      for (j=0; j < ranges[i].num_chars; ++j) {
4236
0
         stbrp_rect *r = &rects[k];
4237
0
         if (r->was_packed && r->w != 0 && r->h != 0) {
4238
0
            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
4239
0
            int advance, lsb, x0,y0,x1,y1;
4240
0
            int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
4241
0
            int glyph = stbtt_FindGlyphIndex(info, codepoint);
4242
0
            stbrp_coord pad = (stbrp_coord) spc->padding;
4243
0
4244
0
            // pad on left and top
4245
0
            r->x += pad;
4246
0
            r->y += pad;
4247
0
            r->w -= pad;
4248
0
            r->h -= pad;
4249
0
            stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
4250
0
            stbtt_GetGlyphBitmapBox(info, glyph,
4251
0
                                    scale * spc->h_oversample,
4252
0
                                    scale * spc->v_oversample,
4253
0
                                    &x0,&y0,&x1,&y1);
4254
0
            stbtt_MakeGlyphBitmapSubpixel(info,
4255
0
                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,
4256
0
                                          r->w - spc->h_oversample+1,
4257
0
                                          r->h - spc->v_oversample+1,
4258
0
                                          spc->stride_in_bytes,
4259
0
                                          scale * spc->h_oversample,
4260
0
                                          scale * spc->v_oversample,
4261
0
                                          0,0,
4262
0
                                          glyph);
4263
0
4264
0
            if (spc->h_oversample > 1)
4265
0
               stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
4266
0
                                  r->w, r->h, spc->stride_in_bytes,
4267
0
                                  spc->h_oversample);
4268
0
4269
0
            if (spc->v_oversample > 1)
4270
0
               stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
4271
0
                                  r->w, r->h, spc->stride_in_bytes,
4272
0
                                  spc->v_oversample);
4273
0
4274
0
            bc->x0       = (stbtt_int16)  r->x;
4275
0
            bc->y0       = (stbtt_int16)  r->y;
4276
0
            bc->x1       = (stbtt_int16) (r->x + r->w);
4277
0
            bc->y1       = (stbtt_int16) (r->y + r->h);
4278
0
            bc->xadvance =                scale * advance;
4279
0
            bc->xoff     =       (float)  x0 * recip_h + sub_x;
4280
0
            bc->yoff     =       (float)  y0 * recip_v + sub_y;
4281
0
            bc->xoff2    =                (x0 + r->w) * recip_h + sub_x;
4282
0
            bc->yoff2    =                (y0 + r->h) * recip_v + sub_y;
4283
0
4284
0
            if (glyph == 0)
4285
0
               missing_glyph = j;
4286
0
         } else if (spc->skip_missing) {
4287
0
            return_value = 0;
4288
0
         } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {
4289
0
            ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];
4290
0
         } else {
4291
0
            return_value = 0; // if any fail, report failure
4292
0
         }
4293
0
4294
0
         ++k;
4295
0
      }
4296
0
   }
4297
0
4298
0
   // restore original values
4299
0
   spc->h_oversample = old_h_over;
4300
0
   spc->v_oversample = old_v_over;
4301
0
4302
0
   return return_value;
4303
0
}
4304
4305
STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
4306
0
{
4307
0
   stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
4308
0
}
4309
4310
STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
4311
0
{
4312
0
   stbtt_fontinfo info;
4313
0
   int i, j, n, return_value; // [DEAR IMGUI] removed = 1;
4314
0
   //stbrp_context *context = (stbrp_context *) spc->pack_info;
4315
0
   stbrp_rect    *rects;
4316
0
4317
0
   // flag all characters as NOT packed
4318
0
   for (i=0; i < num_ranges; ++i)
4319
0
      for (j=0; j < ranges[i].num_chars; ++j)
4320
0
         ranges[i].chardata_for_range[j].x0 =
4321
0
         ranges[i].chardata_for_range[j].y0 =
4322
0
         ranges[i].chardata_for_range[j].x1 =
4323
0
         ranges[i].chardata_for_range[j].y1 = 0;
4324
0
4325
0
   n = 0;
4326
0
   for (i=0; i < num_ranges; ++i)
4327
0
      n += ranges[i].num_chars;
4328
0
4329
0
   rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
4330
0
   if (rects == NULL)
4331
0
      return 0;
4332
0
4333
0
   info.userdata = spc->user_allocator_context;
4334
0
   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
4335
0
4336
0
   n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
4337
0
4338
0
   stbtt_PackFontRangesPackRects(spc, rects, n);
4339
0
4340
0
   return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
4341
0
4342
0
   STBTT_free(rects, spc->user_allocator_context);
4343
0
   return return_value;
4344
0
}
4345
4346
STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
4347
            int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
4348
0
{
4349
0
   stbtt_pack_range range;
4350
0
   range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
4351
0
   range.array_of_unicode_codepoints = NULL;
4352
0
   range.num_chars                   = num_chars_in_range;
4353
0
   range.chardata_for_range          = chardata_for_range;
4354
0
   range.font_size                   = font_size;
4355
0
   return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
4356
0
}
4357
4358
STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)
4359
0
{
4360
0
   int i_ascent, i_descent, i_lineGap;
4361
0
   float scale;
4362
0
   stbtt_fontinfo info;
4363
0
   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));
4364
0
   scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);
4365
0
   stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);
4366
0
   *ascent  = (float) i_ascent  * scale;
4367
0
   *descent = (float) i_descent * scale;
4368
0
   *lineGap = (float) i_lineGap * scale;
4369
0
}
4370
4371
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
4372
0
{
4373
0
   float ipw = 1.0f / pw, iph = 1.0f / ph;
4374
0
   const stbtt_packedchar *b = chardata + char_index;
4375
0
4376
0
   if (align_to_integer) {
4377
0
      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
4378
0
      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
4379
0
      q->x0 = x;
4380
0
      q->y0 = y;
4381
0
      q->x1 = x + b->xoff2 - b->xoff;
4382
0
      q->y1 = y + b->yoff2 - b->yoff;
4383
0
   } else {
4384
0
      q->x0 = *xpos + b->xoff;
4385
0
      q->y0 = *ypos + b->yoff;
4386
0
      q->x1 = *xpos + b->xoff2;
4387
0
      q->y1 = *ypos + b->yoff2;
4388
0
   }
4389
0
4390
0
   q->s0 = b->x0 * ipw;
4391
0
   q->t0 = b->y0 * iph;
4392
0
   q->s1 = b->x1 * ipw;
4393
0
   q->t1 = b->y1 * iph;
4394
0
4395
0
   *xpos += b->xadvance;
4396
0
}
4397
4398
//////////////////////////////////////////////////////////////////////////////
4399
//
4400
// sdf computation
4401
//
4402
4403
#define STBTT_min(a,b)  ((a) < (b) ? (a) : (b))
4404
#define STBTT_max(a,b)  ((a) < (b) ? (b) : (a))
4405
4406
static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])
4407
0
{
4408
0
   float q0perp = q0[1]*ray[0] - q0[0]*ray[1];
4409
0
   float q1perp = q1[1]*ray[0] - q1[0]*ray[1];
4410
0
   float q2perp = q2[1]*ray[0] - q2[0]*ray[1];
4411
0
   float roperp = orig[1]*ray[0] - orig[0]*ray[1];
4412
0
4413
0
   float a = q0perp - 2*q1perp + q2perp;
4414
0
   float b = q1perp - q0perp;
4415
0
   float c = q0perp - roperp;
4416
0
4417
0
   float s0 = 0., s1 = 0.;
4418
0
   int num_s = 0;
4419
0
4420
0
   if (a != 0.0) {
4421
0
      float discr = b*b - a*c;
4422
0
      if (discr > 0.0) {
4423
0
         float rcpna = -1 / a;
4424
0
         float d = (float) STBTT_sqrt(discr);
4425
0
         s0 = (b+d) * rcpna;
4426
0
         s1 = (b-d) * rcpna;
4427
0
         if (s0 >= 0.0 && s0 <= 1.0)
4428
0
            num_s = 1;
4429
0
         if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {
4430
0
            if (num_s == 0) s0 = s1;
4431
0
            ++num_s;
4432
0
         }
4433
0
      }
4434
0
   } else {
4435
0
      // 2*b*s + c = 0
4436
0
      // s = -c / (2*b)
4437
0
      s0 = c / (-2 * b);
4438
0
      if (s0 >= 0.0 && s0 <= 1.0)
4439
0
         num_s = 1;
4440
0
   }
4441
0
4442
0
   if (num_s == 0)
4443
0
      return 0;
4444
0
   else {
4445
0
      float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);
4446
0
      float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;
4447
0
4448
0
      float q0d =   q0[0]*rayn_x +   q0[1]*rayn_y;
4449
0
      float q1d =   q1[0]*rayn_x +   q1[1]*rayn_y;
4450
0
      float q2d =   q2[0]*rayn_x +   q2[1]*rayn_y;
4451
0
      float rod = orig[0]*rayn_x + orig[1]*rayn_y;
4452
0
4453
0
      float q10d = q1d - q0d;
4454
0
      float q20d = q2d - q0d;
4455
0
      float q0rd = q0d - rod;
4456
0
4457
0
      hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;
4458
0
      hits[0][1] = a*s0+b;
4459
0
4460
0
      if (num_s > 1) {
4461
0
         hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;
4462
0
         hits[1][1] = a*s1+b;
4463
0
         return 2;
4464
0
      } else {
4465
0
         return 1;
4466
0
      }
4467
0
   }
4468
0
}
4469
4470
static int equal(float *a, float *b)
4471
0
{
4472
0
   return (a[0] == b[0] && a[1] == b[1]);
4473
0
}
4474
4475
static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
4476
0
{
4477
0
   int i;
4478
0
   float orig[2], ray[2] = { 1, 0 };
4479
0
   float y_frac;
4480
0
   int winding = 0;
4481
0
4482
0
   // make sure y never passes through a vertex of the shape
4483
0
   y_frac = (float) STBTT_fmod(y, 1.0f);
4484
0
   if (y_frac < 0.01f)
4485
0
      y += 0.01f;
4486
0
   else if (y_frac > 0.99f)
4487
0
      y -= 0.01f;
4488
0
4489
0
   orig[0] = x;
4490
0
   orig[1] = y;
4491
0
4492
0
   // test a ray from (-infinity,y) to (x,y)
4493
0
   for (i=0; i < nverts; ++i) {
4494
0
      if (verts[i].type == STBTT_vline) {
4495
0
         int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;
4496
0
         int x1 = (int) verts[i  ].x, y1 = (int) verts[i  ].y;
4497
0
         if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
4498
0
            float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
4499
0
            if (x_inter < x)
4500
0
               winding += (y0 < y1) ? 1 : -1;
4501
0
         }
4502
0
      }
4503
0
      if (verts[i].type == STBTT_vcurve) {
4504
0
         int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;
4505
0
         int x1 = (int) verts[i  ].cx, y1 = (int) verts[i  ].cy;
4506
0
         int x2 = (int) verts[i  ].x , y2 = (int) verts[i  ].y ;
4507
0
         int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));
4508
0
         int by = STBTT_max(y0,STBTT_max(y1,y2));
4509
0
         if (y > ay && y < by && x > ax) {
4510
0
            float q0[2],q1[2],q2[2];
4511
0
            float hits[2][2];
4512
0
            q0[0] = (float)x0;
4513
0
            q0[1] = (float)y0;
4514
0
            q1[0] = (float)x1;
4515
0
            q1[1] = (float)y1;
4516
0
            q2[0] = (float)x2;
4517
0
            q2[1] = (float)y2;
4518
0
            if (equal(q0,q1) || equal(q1,q2)) {
4519
0
               x0 = (int)verts[i-1].x; //-V1048
4520
0
               y0 = (int)verts[i-1].y; //-V1048
4521
0
               x1 = (int)verts[i  ].x;
4522
0
               y1 = (int)verts[i  ].y;
4523
0
               if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
4524
0
                  float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
4525
0
                  if (x_inter < x)
4526
0
                     winding += (y0 < y1) ? 1 : -1;
4527
0
               }
4528
0
            } else {
4529
0
               int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);
4530
0
               if (num_hits >= 1)
4531
0
                  if (hits[0][0] < 0)
4532
0
                     winding += (hits[0][1] < 0 ? -1 : 1);
4533
0
               if (num_hits >= 2)
4534
0
                  if (hits[1][0] < 0)
4535
0
                     winding += (hits[1][1] < 0 ? -1 : 1);
4536
0
            }
4537
0
         }
4538
0
      }
4539
0
   }
4540
0
   return winding;
4541
0
}
4542
4543
static float stbtt__cuberoot( float x )
4544
0
{
4545
0
   if (x<0)
4546
0
      return -(float) STBTT_pow(-x,1.0f/3.0f);
4547
0
   else
4548
0
      return  (float) STBTT_pow( x,1.0f/3.0f);
4549
0
}
4550
4551
// x^3 + a*x^2 + b*x + c = 0
4552
static int stbtt__solve_cubic(float a, float b, float c, float* r)
4553
0
{
4554
0
   float s = -a / 3;
4555
0
   float p = b - a*a / 3;
4556
0
   float q = a * (2*a*a - 9*b) / 27 + c;
4557
0
   float p3 = p*p*p;
4558
0
   float d = q*q + 4*p3 / 27;
4559
0
   if (d >= 0) {
4560
0
      float z = (float) STBTT_sqrt(d);
4561
0
      float u = (-q + z) / 2;
4562
0
      float v = (-q - z) / 2;
4563
0
      u = stbtt__cuberoot(u);
4564
0
      v = stbtt__cuberoot(v);
4565
0
      r[0] = s + u + v;
4566
0
      return 1;
4567
0
   } else {
4568
0
      float u = (float) STBTT_sqrt(-p/3);
4569
0
      float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
4570
0
      float m = (float) STBTT_cos(v);
4571
0
      float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
4572
0
      r[0] = s + u * 2 * m;
4573
0
      r[1] = s - u * (m + n);
4574
0
      r[2] = s - u * (m - n);
4575
0
4576
0
      //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f);  // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
4577
0
      //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
4578
0
      //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
4579
0
      return 3;
4580
0
   }
4581
0
}
4582
4583
STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
4584
0
{
4585
0
   float scale_x = scale, scale_y = scale;
4586
0
   int ix0,iy0,ix1,iy1;
4587
0
   int w,h;
4588
0
   unsigned char *data;
4589
0
4590
0
   if (scale == 0) return NULL;
4591
0
4592
0
   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
4593
0
4594
0
   // if empty, return NULL
4595
0
   if (ix0 == ix1 || iy0 == iy1)
4596
0
      return NULL;
4597
0
4598
0
   ix0 -= padding;
4599
0
   iy0 -= padding;
4600
0
   ix1 += padding;
4601
0
   iy1 += padding;
4602
0
4603
0
   w = (ix1 - ix0);
4604
0
   h = (iy1 - iy0);
4605
0
4606
0
   if (width ) *width  = w;
4607
0
   if (height) *height = h;
4608
0
   if (xoff  ) *xoff   = ix0;
4609
0
   if (yoff  ) *yoff   = iy0;
4610
0
4611
0
   // invert for y-downwards bitmaps
4612
0
   scale_y = -scale_y;
4613
0
4614
0
   {
4615
0
      int x,y,i,j;
4616
0
      float *precompute;
4617
0
      stbtt_vertex *verts;
4618
0
      int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
4619
0
      data = (unsigned char *) STBTT_malloc(w * h, info->userdata);
4620
0
      precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);
4621
0
4622
0
      for (i=0,j=num_verts-1; i < num_verts; j=i++) {
4623
0
         if (verts[i].type == STBTT_vline) {
4624
0
            float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
4625
0
            float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
4626
0
            float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
4627
0
            precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
4628
0
         } else if (verts[i].type == STBTT_vcurve) {
4629
0
            float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
4630
0
            float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
4631
0
            float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
4632
0
            float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
4633
0
            float len2 = bx*bx + by*by;
4634
0
            if (len2 != 0.0f)
4635
0
               precompute[i] = 1.0f / (bx*bx + by*by);
4636
0
            else
4637
0
               precompute[i] = 0.0f;
4638
0
         } else
4639
0
            precompute[i] = 0.0f;
4640
0
      }
4641
0
4642
0
      for (y=iy0; y < iy1; ++y) {
4643
0
         for (x=ix0; x < ix1; ++x) {
4644
0
            float val;
4645
0
            float min_dist = 999999.0f;
4646
0
            float sx = (float) x + 0.5f;
4647
0
            float sy = (float) y + 0.5f;
4648
0
            float x_gspace = (sx / scale_x);
4649
0
            float y_gspace = (sy / scale_y);
4650
0
4651
0
            int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path
4652
0
4653
0
            for (i=0; i < num_verts; ++i) {
4654
0
               float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
4655
0
4656
0
               if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {
4657
0
                  float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
4658
0
4659
0
                  float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
4660
0
                  if (dist2 < min_dist*min_dist)
4661
0
                     min_dist = (float) STBTT_sqrt(dist2);
4662
0
4663
0
                  // coarse culling against bbox
4664
0
                  //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
4665
0
                  //    sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
4666
0
                  dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
4667
0
                  STBTT_assert(i != 0);
4668
0
                  if (dist < min_dist) {
4669
0
                     // check position along line
4670
0
                     // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)
4671
0
                     // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)
4672
0
                     float dx = x1-x0, dy = y1-y0;
4673
0
                     float px = x0-sx, py = y0-sy;
4674
0
                     // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy
4675
0
                     // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve
4676
0
                     float t = -(px*dx + py*dy) / (dx*dx + dy*dy);
4677
0
                     if (t >= 0.0f && t <= 1.0f)
4678
0
                        min_dist = dist;
4679
0
                  }
4680
0
               } else if (verts[i].type == STBTT_vcurve) {
4681
0
                  float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;
4682
0
                  float x1 = verts[i  ].cx*scale_x, y1 = verts[i  ].cy*scale_y;
4683
0
                  float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);
4684
0
                  float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);
4685
0
                  float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);
4686
0
                  float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);
4687
0
                  // coarse culling against bbox to avoid computing cubic unnecessarily
4688
0
                  if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {
4689
0
                     int num=0;
4690
0
                     float ax = x1-x0, ay = y1-y0;
4691
0
                     float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
4692
0
                     float mx = x0 - sx, my = y0 - sy;
4693
0
                     float res[3] = {0.f,0.f,0.f};
4694
0
                     float px,py,t,it,dist2;
4695
0
                     float a_inv = precompute[i];
4696
0
                     if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
4697
0
                        float a = 3*(ax*bx + ay*by);
4698
0
                        float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
4699
0
                        float c = mx*ax+my*ay;
4700
0
                        if (a == 0.0) { // if a is 0, it's linear
4701
0
                           if (b != 0.0) {
4702
0
                              res[num++] = -c/b;
4703
0
                           }
4704
0
                        } else {
4705
0
                           float discriminant = b*b - 4*a*c;
4706
0
                           if (discriminant < 0)
4707
0
                              num = 0;
4708
0
                           else {
4709
0
                              float root = (float) STBTT_sqrt(discriminant);
4710
0
                              res[0] = (-b - root)/(2*a);
4711
0
                              res[1] = (-b + root)/(2*a);
4712
0
                              num = 2; // don't bother distinguishing 1-solution case, as code below will still work
4713
0
                           }
4714
0
                        }
4715
0
                     } else {
4716
0
                        float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point
4717
0
                        float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;
4718
0
                        float d = (mx*ax+my*ay) * a_inv;
4719
0
                        num = stbtt__solve_cubic(b, c, d, res);
4720
0
                     }
4721
0
                     dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
4722
0
                     if (dist2 < min_dist*min_dist)
4723
0
                        min_dist = (float) STBTT_sqrt(dist2);
4724
0
4725
0
                     if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
4726
0
                        t = res[0], it = 1.0f - t;
4727
0
                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;
4728
0
                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;
4729
0
                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
4730
0
                        if (dist2 < min_dist * min_dist)
4731
0
                           min_dist = (float) STBTT_sqrt(dist2);
4732
0
                     }
4733
0
                     if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {
4734
0
                        t = res[1], it = 1.0f - t;
4735
0
                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;
4736
0
                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;
4737
0
                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
4738
0
                        if (dist2 < min_dist * min_dist)
4739
0
                           min_dist = (float) STBTT_sqrt(dist2);
4740
0
                     }
4741
0
                     if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {
4742
0
                        t = res[2], it = 1.0f - t;
4743
0
                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;
4744
0
                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;
4745
0
                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
4746
0
                        if (dist2 < min_dist * min_dist)
4747
0
                           min_dist = (float) STBTT_sqrt(dist2);
4748
0
                     }
4749
0
                  }
4750
0
               }
4751
0
            }
4752
0
            if (winding == 0)
4753
0
               min_dist = -min_dist;  // if outside the shape, value is negative
4754
0
            val = onedge_value + pixel_dist_scale * min_dist;
4755
0
            if (val < 0)
4756
0
               val = 0;
4757
0
            else if (val > 255)
4758
0
               val = 255;
4759
0
            data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;
4760
0
         }
4761
0
      }
4762
0
      STBTT_free(precompute, info->userdata);
4763
0
      STBTT_free(verts, info->userdata);
4764
0
   }
4765
0
   return data;
4766
0
}
4767
4768
STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
4769
0
{
4770
0
   return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);
4771
0
}
4772
4773
STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
4774
0
{
4775
0
   STBTT_free(bitmap, userdata);
4776
0
}
4777
4778
//////////////////////////////////////////////////////////////////////////////
4779
//
4780
// font name matching -- recommended not to use this
4781
//
4782
4783
// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
4784
static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
4785
0
{
4786
0
   stbtt_int32 i=0;
4787
0
4788
0
   // convert utf16 to utf8 and compare the results while converting
4789
0
   while (len2) {
4790
0
      stbtt_uint16 ch = s2[0]*256 + s2[1];
4791
0
      if (ch < 0x80) {
4792
0
         if (i >= len1) return -1;
4793
0
         if (s1[i++] != ch) return -1;
4794
0
      } else if (ch < 0x800) {
4795
0
         if (i+1 >= len1) return -1;
4796
0
         if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
4797
0
         if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
4798
0
      } else if (ch >= 0xd800 && ch < 0xdc00) {
4799
0
         stbtt_uint32 c;
4800
0
         stbtt_uint16 ch2 = s2[2]*256 + s2[3];
4801
0
         if (i+3 >= len1) return -1;
4802
0
         c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
4803
0
         if (s1[i++] != 0xf0 + (c >> 18)) return -1;
4804
0
         if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
4805
0
         if (s1[i++] != 0x80 + ((c >>  6) & 0x3f)) return -1;
4806
0
         if (s1[i++] != 0x80 + ((c      ) & 0x3f)) return -1;
4807
0
         s2 += 2; // plus another 2 below
4808
0
         len2 -= 2;
4809
0
      } else if (ch >= 0xdc00 && ch < 0xe000) {
4810
0
         return -1;
4811
0
      } else {
4812
0
         if (i+2 >= len1) return -1;
4813
0
         if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
4814
0
         if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
4815
0
         if (s1[i++] != 0x80 + ((ch     ) & 0x3f)) return -1;
4816
0
      }
4817
0
      s2 += 2;
4818
0
      len2 -= 2;
4819
0
   }
4820
0
   return i;
4821
0
}
4822
4823
static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
4824
0
{
4825
0
   return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
4826
0
}
4827
4828
// returns results in whatever encoding you request... but note that 2-byte encodings
4829
// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
4830
STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
4831
0
{
4832
0
   stbtt_int32 i,count,stringOffset;
4833
0
   stbtt_uint8 *fc = font->data;
4834
0
   stbtt_uint32 offset = font->fontstart;
4835
0
   stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
4836
0
   if (!nm) return NULL;
4837
0
4838
0
   count = ttUSHORT(fc+nm+2);
4839
0
   stringOffset = nm + ttUSHORT(fc+nm+4);
4840
0
   for (i=0; i < count; ++i) {
4841
0
      stbtt_uint32 loc = nm + 6 + 12 * i;
4842
0
      if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
4843
0
          && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
4844
0
         *length = ttUSHORT(fc+loc+8);
4845
0
         return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
4846
0
      }
4847
0
   }
4848
0
   return NULL;
4849
0
}
4850
4851
static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
4852
0
{
4853
0
   stbtt_int32 i;
4854
0
   stbtt_int32 count = ttUSHORT(fc+nm+2);
4855
0
   stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
4856
0
4857
0
   for (i=0; i < count; ++i) {
4858
0
      stbtt_uint32 loc = nm + 6 + 12 * i;
4859
0
      stbtt_int32 id = ttUSHORT(fc+loc+6);
4860
0
      if (id == target_id) {
4861
0
         // find the encoding
4862
0
         stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
4863
0
4864
0
         // is this a Unicode encoding?
4865
0
         if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
4866
0
            stbtt_int32 slen = ttUSHORT(fc+loc+8);
4867
0
            stbtt_int32 off = ttUSHORT(fc+loc+10);
4868
0
4869
0
            // check if there's a prefix match
4870
0
            stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
4871
0
            if (matchlen >= 0) {
4872
0
               // check for target_id+1 immediately following, with same encoding & language
4873
0
               if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
4874
0
                  slen = ttUSHORT(fc+loc+12+8);
4875
0
                  off = ttUSHORT(fc+loc+12+10);
4876
0
                  if (slen == 0) {
4877
0
                     if (matchlen == nlen)
4878
0
                        return 1;
4879
0
                  } else if (matchlen < nlen && name[matchlen] == ' ') {
4880
0
                     ++matchlen;
4881
0
                     if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
4882
0
                        return 1;
4883
0
                  }
4884
0
               } else {
4885
0
                  // if nothing immediately following
4886
0
                  if (matchlen == nlen)
4887
0
                     return 1;
4888
0
               }
4889
0
            }
4890
0
         }
4891
0
4892
0
         // @TODO handle other encodings
4893
0
      }
4894
0
   }
4895
0
   return 0;
4896
0
}
4897
4898
static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
4899
0
{
4900
0
   stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
4901
0
   stbtt_uint32 nm,hd;
4902
0
   if (!stbtt__isfont(fc+offset)) return 0;
4903
0
4904
0
   // check italics/bold/underline flags in macStyle...
4905
0
   if (flags) {
4906
0
      hd = stbtt__find_table(fc, offset, "head");
4907
0
      if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
4908
0
   }
4909
0
4910
0
   nm = stbtt__find_table(fc, offset, "name");
4911
0
   if (!nm) return 0;
4912
0
4913
0
   if (flags) {
4914
0
      // if we checked the macStyle flags, then just check the family and ignore the subfamily
4915
0
      if (stbtt__matchpair(fc, nm, name, nlen, 16, -1))  return 1;
4916
0
      if (stbtt__matchpair(fc, nm, name, nlen,  1, -1))  return 1;
4917
0
      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;
4918
0
   } else {
4919
0
      if (stbtt__matchpair(fc, nm, name, nlen, 16, 17))  return 1;
4920
0
      if (stbtt__matchpair(fc, nm, name, nlen,  1,  2))  return 1;
4921
0
      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;
4922
0
   }
4923
0
4924
0
   return 0;
4925
0
}
4926
4927
static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)
4928
0
{
4929
0
   stbtt_int32 i;
4930
0
   for (i=0;;++i) {
4931
0
      stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
4932
0
      if (off < 0) return off;
4933
0
      if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
4934
0
         return off;
4935
0
   }
4936
0
}
4937
4938
#if defined(__GNUC__) || defined(__clang__)
4939
#pragma GCC diagnostic push
4940
#pragma GCC diagnostic ignored "-Wcast-qual"
4941
#endif
4942
4943
STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
4944
                                float pixel_height, unsigned char *pixels, int pw, int ph,
4945
                                int first_char, int num_chars, stbtt_bakedchar *chardata)
4946
0
{
4947
0
   return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);
4948
0
}
4949
4950
STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
4951
1
{
4952
1
   return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
4953
1
}
4954
4955
STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
4956
0
{
4957
0
   return stbtt_GetNumberOfFonts_internal((unsigned char *) data);
4958
0
}
4959
4960
STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)
4961
1
{
4962
1
   return stbtt_InitFont_internal(info, (unsigned char *) data, offset);
4963
1
}
4964
4965
STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)
4966
0
{
4967
0
   return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);
4968
0
}
4969
4970
STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
4971
0
{
4972
0
   return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);
4973
0
}
4974
4975
#if defined(__GNUC__) || defined(__clang__)
4976
#pragma GCC diagnostic pop
4977
#endif
4978
4979
#endif // STB_TRUETYPE_IMPLEMENTATION
4980
4981
4982
// FULL VERSION HISTORY
4983
//
4984
//   1.25 (2021-07-11) many fixes
4985
//   1.24 (2020-02-05) fix warning
4986
//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
4987
//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
4988
//   1.21 (2019-02-25) fix warning
4989
//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
4990
//   1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
4991
//   1.18 (2018-01-29) add missing function
4992
//   1.17 (2017-07-23) make more arguments const; doc fix
4993
//   1.16 (2017-07-12) SDF support
4994
//   1.15 (2017-03-03) make more arguments const
4995
//   1.14 (2017-01-16) num-fonts-in-TTC function
4996
//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
4997
//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
4998
//   1.11 (2016-04-02) fix unused-variable warning
4999
//   1.10 (2016-04-02) allow user-defined fabs() replacement
5000
//                     fix memory leak if fontsize=0.0
5001
//                     fix warning from duplicate typedef
5002
//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
5003
//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
5004
//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
5005
//                     allow PackFontRanges to pack and render in separate phases;
5006
//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
5007
//                     fixed an assert() bug in the new rasterizer
5008
//                     replace assert() with STBTT_assert() in new rasterizer
5009
//   1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
5010
//                     also more precise AA rasterizer, except if shapes overlap
5011
//                     remove need for STBTT_sort
5012
//   1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
5013
//   1.04 (2015-04-15) typo in example
5014
//   1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
5015
//   1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
5016
//   1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
5017
//                        non-oversampled; STBTT_POINT_SIZE for packed case only
5018
//   1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
5019
//   0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
5020
//   0.9  (2014-08-07) support certain mac/iOS fonts without an MS platformID
5021
//   0.8b (2014-07-07) fix a warning
5022
//   0.8  (2014-05-25) fix a few more warnings
5023
//   0.7  (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
5024
//   0.6c (2012-07-24) improve documentation
5025
//   0.6b (2012-07-20) fix a few more warnings
5026
//   0.6  (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
5027
//                        stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
5028
//   0.5  (2011-12-09) bugfixes:
5029
//                        subpixel glyph renderer computed wrong bounding box
5030
//                        first vertex of shape can be off-curve (FreeSans)
5031
//   0.4b (2011-12-03) fixed an error in the font baking example
5032
//   0.4  (2011-12-01) kerning, subpixel rendering (tor)
5033
//                    bugfixes for:
5034
//                        codepoint-to-glyph conversion using table fmt=12
5035
//                        codepoint-to-glyph conversion using table fmt=4
5036
//                        stbtt_GetBakedQuad with non-square texture (Zer)
5037
//                    updated Hello World! sample to use kerning and subpixel
5038
//                    fixed some warnings
5039
//   0.3  (2009-06-24) cmap fmt=12, compound shapes (MM)
5040
//                    userdata, malloc-from-userdata, non-zero fill (stb)
5041
//   0.2  (2009-03-11) Fix unsigned/signed char warnings
5042
//   0.1  (2009-03-09) First public release
5043
//
5044
5045
/*
5046
------------------------------------------------------------------------------
5047
This software is available under 2 licenses -- choose whichever you prefer.
5048
------------------------------------------------------------------------------
5049
ALTERNATIVE A - MIT License
5050
Copyright (c) 2017 Sean Barrett
5051
Permission is hereby granted, free of charge, to any person obtaining a copy of
5052
this software and associated documentation files (the "Software"), to deal in
5053
the Software without restriction, including without limitation the rights to
5054
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
5055
of the Software, and to permit persons to whom the Software is furnished to do
5056
so, subject to the following conditions:
5057
The above copyright notice and this permission notice shall be included in all
5058
copies or substantial portions of the Software.
5059
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5060
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5061
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5062
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
5063
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
5064
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
5065
SOFTWARE.
5066
------------------------------------------------------------------------------
5067
ALTERNATIVE B - Public Domain (www.unlicense.org)
5068
This is free and unencumbered software released into the public domain.
5069
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
5070
software, either in source code form or as a compiled binary, for any purpose,
5071
commercial or non-commercial, and by any means.
5072
In jurisdictions that recognize copyright laws, the author or authors of this
5073
software dedicate any and all copyright interest in the software to the public
5074
domain. We make this dedication for the benefit of the public at large and to
5075
the detriment of our heirs and successors. We intend this dedication to be an
5076
overt act of relinquishment in perpetuity of all present and future rights to
5077
this software under copyright law.
5078
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5079
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5080
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5081
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
5082
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
5083
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5084
------------------------------------------------------------------------------
5085
*/
/Users/kiltum/projects/zxcpp/lib/imguifiledialog/ImGuiFileDialog.cpp
Line
Count
Source
1
2
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
3
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
4
5
/*
6
MIT License
7
8
Copyright (c) 2019-2024 Stephane Cuillerdier (aka aiekick)
9
10
Permission is hereby granted, free of charge, to any person obtaining a copy
11
of this software and associated documentation files (the "Software"), to deal
12
in the Software without restriction, including without limitation the rights
13
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
copies of the Software, and to permit persons to whom the Software is
15
furnished to do so, subject to the following conditions:
16
17
The above copyright notice and this permission notice shall be included in all
18
copies or substantial portions of the Software.
19
20
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
SOFTWARE.
27
*/
28
29
#include "ImGuiFileDialog.h"
30
31
#ifdef __cplusplus
32
33
#include <cstring>  // stricmp / strcasecmp
34
#include <cstdarg>  // variadic
35
#include <sstream>
36
#include <iomanip>
37
#include <ctime>
38
#include <memory>
39
#include <sys/stat.h>
40
#include <cstdio>
41
#include <cerrno>
42
43
// this option need c++17
44
#ifdef USE_STD_FILESYSTEM
45
#include <filesystem>
46
#include <exception>
47
#endif  // USE_STD_FILESYSTEM
48
49
#ifdef __EMSCRIPTEN__
50
#include <emscripten.h>
51
#endif  // __EMSCRIPTEN__
52
53
#ifdef _MSC_VER
54
55
#define IGFD_DEBUG_BREAK \
56
    if (IsDebuggerPresent()) __debugbreak()
57
#else
58
#define IGFD_DEBUG_BREAK
59
#endif
60
61
#if defined(__WIN32__) || \
62
    defined(WIN32) || \
63
    defined(_WIN32) || \
64
    defined(__WIN64__) || \
65
    defined(WIN64) || \
66
    defined(_WIN64) || \
67
    defined(_MSC_VER)
68
#define _IGFD_WIN_
69
#define stat _stat64
70
#define stricmp _stricmp
71
#include <cctype>
72
// this option need c++17
73
#ifdef USE_STD_FILESYSTEM
74
#include <windows.h>
75
#else                       // USE_STD_FILESYSTEM
76
#include "dirent/dirent.h"  // directly open the dirent file attached to this lib
77
#endif                      // USE_STD_FILESYSTEM
78
#define PATH_SEP '\\'
79
#ifndef PATH_MAX
80
#define PATH_MAX 260
81
#endif  // PATH_MAX
82
#elif defined(__linux__) || \
83
    defined(__FreeBSD__) || \
84
    defined(__DragonFly__) || \
85
    defined(__NetBSD__) || \
86
    defined(__OpenBSD__) || \
87
    defined(__APPLE__) ||\
88
    defined(__EMSCRIPTEN__)
89
#define _IGFD_UNIX_
90
0
#define stricmp strcasecmp
91
#include <sys/types.h>
92
// this option need c++17
93
#ifndef USE_STD_FILESYSTEM
94
#include <dirent.h>
95
#endif  // USE_STD_FILESYSTEM
96
1
#define PATH_SEP '/'
97
#endif  // _IGFD_UNIX_
98
99
100
#ifdef IMGUI_INTERNAL_INCLUDE
101
#include IMGUI_INTERNAL_INCLUDE
102
#else  // IMGUI_INTERNAL_INCLUDE
103
#include <imgui_internal.h>
104
#endif  // IMGUI_INTERNAL_INCLUDE
105
106
// legacy compatibility 1.89
107
#ifndef IM_TRUNC
108
#define IM_TRUNC IM_FLOOR
109
#endif
110
111
#include <cstdlib>
112
#include <algorithm>
113
#include <iostream>
114
115
///////////////////////////////
116
// STB IMAGE LIBS
117
///////////////////////////////
118
119
#ifdef USE_THUMBNAILS
120
#ifndef DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
121
#ifndef STB_IMAGE_IMPLEMENTATION
122
#define STB_IMAGE_IMPLEMENTATION
123
#endif  // STB_IMAGE_IMPLEMENTATION
124
#endif  // DONT_DEFINE_AGAIN__STB_IMAGE_IMPLEMENTATION
125
#include "stb/stb_image.h"
126
#ifndef DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
127
#ifndef STB_IMAGE_RESIZE_IMPLEMENTATION
128
#define STB_IMAGE_RESIZE_IMPLEMENTATION
129
#endif  // STB_IMAGE_RESIZE_IMPLEMENTATION
130
#endif  // DONT_DEFINE_AGAIN__STB_IMAGE_RESIZE_IMPLEMENTATION
131
#include "stb/stb_image_resize2.h"
132
#endif  // USE_THUMBNAILS
133
134
///////////////////////////////
135
// FLOAT MACROS
136
///////////////////////////////
137
138
// float comparisons
139
#ifndef IS_FLOAT_DIFFERENT
140
#define IS_FLOAT_DIFFERENT(a, b) (fabs((a) - (b)) > FLT_EPSILON)
141
#endif  // IS_FLOAT_DIFFERENT
142
#ifndef IS_FLOAT_EQUAL
143
#define IS_FLOAT_EQUAL(a, b) (fabs((a) - (b)) < FLT_EPSILON)
144
#endif  // IS_FLOAT_EQUAL
145
146
///////////////////////////////
147
// COMBOBOX
148
///////////////////////////////
149
150
#ifndef FILTER_COMBO_AUTO_SIZE
151
#define FILTER_COMBO_AUTO_SIZE 1
152
#endif  // FILTER_COMBO_AUTO_SIZE
153
#ifndef FILTER_COMBO_MIN_WIDTH
154
0
#define FILTER_COMBO_MIN_WIDTH 150.0f
155
#endif  // FILTER_COMBO_MIN_WIDTH
156
#ifndef IMGUI_BEGIN_COMBO
157
0
#define IMGUI_BEGIN_COMBO ImGui::BeginCombo
158
#endif  // IMGUI_BEGIN_COMBO
159
160
///////////////////////////////
161
// BUTTON
162
///////////////////////////////
163
164
// for lets you define your button widget
165
// if you have like me a special bi-color button
166
#ifndef IMGUI_PATH_BUTTON
167
0
#define IMGUI_PATH_BUTTON ImGui::Button
168
#endif  // IMGUI_PATH_BUTTON
169
#ifndef IMGUI_BUTTON
170
0
#define IMGUI_BUTTON ImGui::Button
171
#endif  // IMGUI_BUTTON
172
173
///////////////////////////////
174
// locales
175
///////////////////////////////
176
177
#ifndef createDirButtonString
178
0
#define createDirButtonString "+"
179
#endif  // createDirButtonString
180
#ifndef okButtonString
181
0
#define okButtonString "OK"
182
#endif  // okButtonString
183
#ifndef okButtonWidth
184
0
#define okButtonWidth 0.0f
185
#endif  // okButtonWidth
186
#ifndef cancelButtonString
187
0
#define cancelButtonString "Cancel"
188
#endif  // cancelButtonString
189
#ifndef cancelButtonWidth
190
0
#define cancelButtonWidth 0.0f
191
#endif  // cancelButtonWidth
192
#ifndef okCancelButtonAlignement
193
0
#define okCancelButtonAlignement 0.0f
194
#endif  // okCancelButtonAlignement
195
#ifndef invertOkAndCancelButtons
196
// 0 => disabled, 1 => enabled
197
0
#define invertOkAndCancelButtons 0
198
#endif  // invertOkAndCancelButtons
199
#ifndef resetButtonString
200
0
#define resetButtonString "R"
201
#endif  // resetButtonString
202
#ifndef devicesButtonString
203
0
#define devicesButtonString "Devices"
204
#endif  // devicesButtonString
205
#ifndef editPathButtonString
206
0
#define editPathButtonString "E"
207
#endif  // editPathButtonString
208
#ifndef searchString
209
0
#define searchString "Search :"
210
#endif  // searchString
211
#ifndef dirEntryString
212
0
#define dirEntryString "[Dir]"
213
#endif  // dirEntryString
214
#ifndef linkEntryString
215
0
#define linkEntryString "[Link]"
216
#endif  // linkEntryString
217
#ifndef fileEntryString
218
0
#define fileEntryString "[File]"
219
#endif  // fileEntryString
220
#ifndef fileNameString
221
0
#define fileNameString "File Name:"
222
#endif  // fileNameString
223
#ifndef dirNameString
224
0
#define dirNameString "Directory Path:"
225
#endif  // dirNameString
226
#ifndef buttonResetSearchString
227
0
#define buttonResetSearchString "Reset search"
228
#endif  // buttonResetSearchString
229
#ifndef buttonDriveString
230
0
#define buttonDriveString "Devices"
231
#endif  // buttonDriveString
232
#ifndef buttonEditPathString
233
0
#define buttonEditPathString "Edit path\nYou can also right click on path buttons"
234
#endif  // buttonEditPathString
235
#ifndef buttonResetPathString
236
0
#define buttonResetPathString "Reset to current directory"
237
#endif  // buttonResetPathString
238
#ifndef buttonCreateDirString
239
0
#define buttonCreateDirString "Create Directory"
240
#endif  // buttonCreateDirString
241
#ifndef tableHeaderAscendingIcon
242
#define tableHeaderAscendingIcon "A|"
243
#endif  // tableHeaderAscendingIcon
244
#ifndef tableHeaderDescendingIcon
245
#define tableHeaderDescendingIcon "D|"
246
#endif  // tableHeaderDescendingIcon
247
#ifndef tableHeaderFileNameString
248
0
#define tableHeaderFileNameString "File name"
249
#endif  // tableHeaderFileNameString
250
#ifndef tableHeaderFileTypeString
251
0
#define tableHeaderFileTypeString "Type"
252
#endif  // tableHeaderFileTypeString
253
#ifndef tableHeaderFileSizeString
254
0
#define tableHeaderFileSizeString "Size"
255
#endif  // tableHeaderFileSizeString
256
#ifndef tableHeaderFileDateString
257
0
#define tableHeaderFileDateString "Date"
258
#endif  // tableHeaderFileDateString
259
#ifndef fileSizeBytes
260
0
#define fileSizeBytes "o"
261
#endif  // fileSizeBytes
262
#ifndef fileSizeKiloBytes
263
0
#define fileSizeKiloBytes "Ko"
264
#endif  // fileSizeKiloBytes
265
#ifndef fileSizeMegaBytes
266
0
#define fileSizeMegaBytes "Mo"
267
#endif  // fileSizeMegaBytes
268
#ifndef fileSizeGigaBytes
269
0
#define fileSizeGigaBytes "Go"
270
#endif  // fileSizeGigaBytes
271
#ifndef OverWriteDialogTitleString
272
0
#define OverWriteDialogTitleString "The selected file already exists!"
273
#endif  // OverWriteDialogTitleString
274
#ifndef OverWriteDialogMessageString
275
0
#define OverWriteDialogMessageString "Are you sure you want to overwrite it?"
276
#endif  // OverWriteDialogMessageString
277
#ifndef OverWriteDialogConfirmButtonString
278
0
#define OverWriteDialogConfirmButtonString "Confirm"
279
#endif  // OverWriteDialogConfirmButtonString
280
#ifndef OverWriteDialogCancelButtonString
281
0
#define OverWriteDialogCancelButtonString "Cancel"
282
#endif  // OverWriteDialogCancelButtonString
283
#ifndef DateTimeFormat
284
// see strftime functionin <ctime> for customize
285
0
#define DateTimeFormat "%Y/%m/%d %H:%M"
286
#endif  // DateTimeFormat
287
288
///////////////////////////////
289
//// SHORTCUTS => ctrl + KEY
290
///////////////////////////////
291
292
#ifndef SelectAllFilesKey
293
0
#define SelectAllFilesKey ImGuiKey_A
294
#endif  // SelectAllFilesKey
295
296
///////////////////////////////
297
// THUMBNAILS
298
///////////////////////////////
299
300
#ifdef USE_THUMBNAILS
301
#ifndef tableHeaderFileThumbnailsString
302
#define tableHeaderFileThumbnailsString "Thumbnails"
303
#endif  // tableHeaderFileThumbnailsString
304
#ifndef DisplayMode_FilesList_ButtonString
305
#define DisplayMode_FilesList_ButtonString "FL"
306
#endif  // DisplayMode_FilesList_ButtonString
307
#ifndef DisplayMode_FilesList_ButtonHelp
308
#define DisplayMode_FilesList_ButtonHelp "File List"
309
#endif  // DisplayMode_FilesList_ButtonHelp
310
#ifndef DisplayMode_ThumbailsList_ButtonString
311
#define DisplayMode_ThumbailsList_ButtonString "TL"
312
#endif  // DisplayMode_ThumbailsList_ButtonString
313
#ifndef DisplayMode_ThumbailsList_ButtonHelp
314
#define DisplayMode_ThumbailsList_ButtonHelp "Thumbnails List"
315
#endif  // DisplayMode_ThumbailsList_ButtonHelp
316
#ifndef DisplayMode_ThumbailsGrid_ButtonString
317
#define DisplayMode_ThumbailsGrid_ButtonString "TG"
318
#endif  // DisplayMode_ThumbailsGrid_ButtonString
319
#ifndef DisplayMode_ThumbailsGrid_ButtonHelp
320
#define DisplayMode_ThumbailsGrid_ButtonHelp "Thumbnails Grid"
321
#endif  // DisplayMode_ThumbailsGrid_ButtonHelp
322
#ifndef DisplayMode_ThumbailsList_ImageHeight
323
#define DisplayMode_ThumbailsList_ImageHeight 32.0f
324
#endif  // DisplayMode_ThumbailsList_ImageHeight
325
#ifndef IMGUI_RADIO_BUTTON
326
inline bool inRadioButton(const char* vLabel, bool vToggled) {
327
    bool pressed = false;
328
    if (vToggled) {
329
        ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
330
        ImVec4 te  = ImGui::GetStyleColorVec4(ImGuiCol_Text);
331
        ImGui::PushStyleColor(ImGuiCol_Button, te);
332
        ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
333
        ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
334
        ImGui::PushStyleColor(ImGuiCol_Text, bua);
335
    }
336
    pressed = IMGUI_BUTTON(vLabel);
337
    if (vToggled) {
338
        ImGui::PopStyleColor(4);  //-V112
339
    }
340
    return pressed;
341
}
342
#define IMGUI_RADIO_BUTTON inRadioButton
343
#endif  // IMGUI_RADIO_BUTTON
344
#endif  // USE_THUMBNAILS
345
346
///////////////////////////////
347
// PLACES
348
///////////////////////////////
349
350
#ifdef USE_PLACES_FEATURE
351
#ifndef defaultPlacePaneWith
352
#define defaultPlacePaneWith 150.0f
353
#endif  // defaultPlacePaneWith
354
#ifndef placesButtonString
355
#define placesButtonString "Places"
356
#endif  // placesButtonString
357
#ifndef placesButtonHelpString
358
#define placesButtonHelpString "Places"
359
#endif  // placesButtonHelpString
360
#ifndef placesBookmarksGroupName
361
#define placesBookmarksGroupName "Bookmarks"
362
#endif  // placesBookmarksGroupName
363
#ifndef PLACES_BOOKMARK_DEFAULT_OPEPEND
364
#define PLACES_BOOKMARK_DEFAULT_OPEPEND true
365
#endif  // PLACES_BOOKMARK_DEFAULT_OPEPEND
366
#ifndef PLACES_DEVICES_DEFAULT_OPEPEND
367
#define PLACES_DEVICES_DEFAULT_OPEPEND true
368
#endif  // PLACES_DEVICES_DEFAULT_OPEPEND
369
#ifndef placesBookmarksDisplayOrder
370
#define placesBookmarksDisplayOrder 0
371
#endif  // placesBookmarksDisplayOrder
372
#ifndef placesDevicesGroupName
373
#define placesDevicesGroupName "Devices"
374
#endif  // placesDevicesGroupName
375
#ifndef placesDevicesDisplayOrder
376
#define placesDevicesDisplayOrder 10
377
#endif  // placesDevicesDisplayOrder
378
#ifndef addPlaceButtonString
379
#define addPlaceButtonString "+"
380
#endif  // addPlaceButtonString
381
#ifndef removePlaceButtonString
382
#define removePlaceButtonString "-"
383
#endif  // removePlaceButtonString
384
#ifndef validatePlaceButtonString
385
#define validatePlaceButtonString "ok"
386
#endif  // validatePlaceButtonString
387
#ifndef editPlaceButtonString
388
#define editPlaceButtonString "E"
389
#endif  // editPlaceButtonString
390
#ifndef PLACES_PANE_DEFAULT_SHOWN
391
#define PLACES_PANE_DEFAULT_SHOWN false
392
#endif  // PLACES_PANE_DEFAULT_SHOWN
393
#ifndef IMGUI_TOGGLE_BUTTON
394
inline bool inToggleButton(const char* vLabel, bool* vToggled) {
395
    bool pressed = false;
396
397
    if (vToggled && *vToggled) {
398
        ImVec4 bua = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
399
        // ImVec4 buh = ImGui::GetStyleColorVec4(ImGuiCol_ButtonHovered);
400
        // ImVec4 bu = ImGui::GetStyleColorVec4(ImGuiCol_Button);
401
        ImVec4 te = ImGui::GetStyleColorVec4(ImGuiCol_Text);
402
        ImGui::PushStyleColor(ImGuiCol_Button, te);
403
        ImGui::PushStyleColor(ImGuiCol_ButtonActive, te);
404
        ImGui::PushStyleColor(ImGuiCol_ButtonHovered, te);
405
        ImGui::PushStyleColor(ImGuiCol_Text, bua);
406
    }
407
408
    pressed = IMGUI_BUTTON(vLabel);
409
410
    if (vToggled && *vToggled) {
411
        ImGui::PopStyleColor(4);  //-V112
412
    }
413
414
    if (vToggled && pressed) *vToggled = !*vToggled;
415
416
    return pressed;
417
}
418
#define IMGUI_TOGGLE_BUTTON inToggleButton
419
#endif  // IMGUI_TOGGLE_BUTTON
420
#endif  // USE_PLACES_FEATURE
421
422
class IGFDException : public std::exception {
423
private:
424
    char const* m_msg{};
425
426
public:
427
0
    IGFDException() : std::exception() {
428
0
    }
429
    explicit IGFDException(char const* const vMsg)
430
0
        : std::exception(),  // std::exception(msg) is not availaiable on linux it seems... but on windos yes
431
0
          m_msg(vMsg) {
432
0
    }
433
0
    char const* what() const noexcept override {
434
0
        return m_msg;
435
0
    }
436
};
437
438
#ifndef CUSTOM_FILESYSTEM_INCLUDE
439
#ifdef USE_STD_FILESYSTEM
440
441
static std::filesystem::path stringToPath(const std::string& str) {
442
#ifdef _IGFD_WIN_
443
    return std::filesystem::path(IGFD::Utils::UTF8Decode(str));
444
#else
445
    return std::filesystem::path(str);
446
#endif
447
}
448
449
static std::string pathToString(const std::filesystem::path& path) {
450
#ifdef _IGFD_WIN_
451
    return IGFD::Utils::UTF8Encode(path.wstring());
452
#else
453
    return path.string();
454
#endif
455
}
456
457
class FileSystemStd : public IGFD::IFileSystem {
458
public:
459
    bool IsDirectoryCanBeOpened(const std::string& vName) override {
460
        bool bExists = false;
461
        if (!vName.empty()) {
462
            namespace fs  = std::filesystem;
463
            auto pathName = stringToPath(vName);
464
            try {
465
                // interesting, in the case of a protected dir or for any reason the dir cant be opened
466
                // this func will work but will say nothing more . not like the dirent version
467
                bExists = fs::is_directory(pathName);
468
                // test if can be opened, this function can thrown an exception if there is an issue with this dir
469
                // here, the dir_iter is need else not exception is thrown..
470
                const auto dir_iter = fs::directory_iterator(pathName);
471
                (void)dir_iter;  // for avoid unused warnings
472
            } catch (const std::exception& /*ex*/) {
473
                // fail so this dir cant be opened
474
                bExists = false;
475
            }
476
        }
477
        return bExists;  // this is not a directory!
478
    }
479
    bool IsDirectoryExist(const std::string& vName) override {
480
        if (!vName.empty()) {
481
            namespace fs = std::filesystem;
482
            return fs::is_directory(stringToPath(vName));
483
        }
484
        return false;  // this is not a directory!
485
    }
486
    bool IsFileExist(const std::string& vName) override {
487
        namespace fs = std::filesystem;
488
        return fs::is_regular_file(stringToPath(vName));
489
    }
490
    bool CreateDirectoryIfNotExist(const std::string& vName) override {
491
        if (vName.empty()) return false;
492
        if (IsDirectoryExist(vName)) return true;
493
494
#if defined(__EMSCRIPTEN__)
495
        std::string str = std::string("FS.mkdir('") + vName + "');";
496
        emscripten_run_script(str.c_str());
497
        bool res = true;
498
#else
499
        namespace fs = std::filesystem;
500
        bool res     = fs::create_directory(stringToPath(vName));
501
#endif  // _IGFD_WIN_
502
        if (!res) {
503
            std::cout << "Error creating directory " << vName << std::endl;
504
        }
505
        return res;
506
    }
507
508
    std::vector<IGFD::PathDisplayedName> GetDevicesList() override {
509
        std::vector<IGFD::PathDisplayedName> res;
510
#ifdef _IGFD_WIN_
511
        const DWORD mydevices = 2048;
512
        char lpBuffer[2048];
513
#define mini(a, b) (((a) < (b)) ? (a) : (b))
514
        const DWORD countChars = mini(GetLogicalDriveStringsA(mydevices, lpBuffer), 2047);
515
#undef mini
516
        if (countChars > 0U && countChars < 2049U) {
517
            std::string var = std::string(lpBuffer, (size_t)countChars);
518
            IGFD::Utils::ReplaceString(var, "\\", "");
519
            auto arr = IGFD::Utils::SplitStringToVector(var, '\0', false);
520
            wchar_t szVolumeName[2048];
521
            IGFD::PathDisplayedName path_name;
522
            for (auto& a : arr) {
523
                path_name.first = a;
524
                path_name.second.clear();
525
                std::wstring wpath = IGFD::Utils::UTF8Decode(a);
526
                if (GetVolumeInformationW(wpath.c_str(), szVolumeName, 2048, nullptr, nullptr, nullptr, nullptr, 0)) {
527
                    path_name.second = IGFD::Utils::UTF8Encode(szVolumeName);
528
                    res.push_back(path_name);
529
                }
530
            }
531
        }
532
#endif  // _IGFD_WIN_
533
        return res;
534
    }
535
536
    IGFD::Utils::PathStruct ParsePathFileName(const std::string& vPathFileName) override {
537
        // https://github.com/aiekick/ImGuiFileDialog/issues/54
538
        namespace fs = std::filesystem;
539
        IGFD::Utils::PathStruct res;
540
        if (vPathFileName.empty()) return res;
541
        auto fsPath = stringToPath(vPathFileName);
542
        if (fs::is_directory(fsPath)) {
543
            res.name = "";
544
            res.path = pathToString(fsPath);
545
            res.isOk = true;
546
        } else if (fs::is_regular_file(fsPath)) {
547
            res.name = pathToString(fsPath.filename());
548
            res.path = pathToString(fsPath.parent_path());
549
            res.isOk = true;
550
        }
551
        return res;
552
    }
553
554
    std::vector<IGFD::FileInfos> ScanDirectory(const std::string& vPath) override {
555
        std::vector<IGFD::FileInfos> res;
556
        try {
557
            namespace fs          = std::filesystem;
558
            auto fspath           = stringToPath(vPath);
559
            const auto dir_iter   = fs::directory_iterator(fspath);
560
            IGFD::FileType fstype = IGFD::FileType(IGFD::FileType::ContentType::Directory, fs::is_symlink(fs::status(fspath)));
561
            {
562
                IGFD::FileInfos file_two_dot;
563
                file_two_dot.filePath    = vPath;
564
                file_two_dot.fileNameExt = "..";
565
                file_two_dot.fileType    = fstype;
566
                res.push_back(file_two_dot);
567
            }
568
            for (const auto& file : dir_iter) {
569
                try {
570
                    IGFD::FileType fileType;
571
                    if (file.is_symlink()) {
572
                        fileType.SetSymLink(file.is_symlink());
573
                        fileType.SetContent(IGFD::FileType::ContentType::LinkToUnknown);
574
                    }
575
                    if (file.is_directory()) {
576
                        fileType.SetContent(IGFD::FileType::ContentType::Directory);
577
                    }  // directory or symlink to directory
578
                    else if (file.is_regular_file()) {
579
                        fileType.SetContent(IGFD::FileType::ContentType::File);
580
                    }
581
                    if (fileType.isValid()) {
582
                        auto fileNameExt = pathToString(file.path().filename());
583
                        {
584
                            IGFD::FileInfos _file;
585
                            _file.filePath    = vPath;
586
                            _file.fileNameExt = fileNameExt;
587
                            _file.fileType    = fileType;
588
                            res.push_back(_file);
589
                        }
590
                    }
591
                } catch (const std::exception& ex) {
592
                    std::cout << "IGFD : " << ex.what() << std::endl;
593
                }
594
            }
595
        } catch (const std::exception& ex) {
596
            std::cout << "IGFD : " << ex.what() << std::endl;
597
        }
598
        return res;
599
    }
600
    bool IsDirectory(const std::string& vFilePathName) override {
601
        namespace fs = std::filesystem;
602
        return fs::is_directory(stringToPath(vFilePathName));
603
    }
604
    void GetFileDateAndSize(const std::string& vFilePathName, const IGFD::FileType& vFileType, std::string& voDate, size_t& voSize) override {
605
        namespace fs = std::filesystem;
606
        fs::path fpath(vFilePathName);
607
        try {
608
            // date
609
            size_t len{};
610
            const auto lastWriteTime = fs::last_write_time(fpath);
611
            const auto sctp          = std::chrono::time_point_cast<std::chrono::system_clock::duration>(  //
612
                lastWriteTime - fs::file_time_type::clock::now() + std::chrono::system_clock::now());
613
            const auto cftime        = std::chrono::system_clock::to_time_t(sctp);
614
            static char timebuf[100];
615
            std::strftime(timebuf, sizeof(timebuf), DateTimeFormat, std::localtime(&cftime));
616
            voDate = timebuf;
617
            // size
618
            if (!vFileType.isDir()) {
619
                voSize = fs::file_size(fpath);
620
            }
621
        } catch (const fs::filesystem_error& e) {
622
            voSize = 0;
623
            voDate.clear();
624
        }
625
    }
626
};
627
#define FILE_SYSTEM_OVERRIDE FileSystemStd
628
#else
629
class FileSystemDirent : public IGFD::IFileSystem {
630
public:
631
0
    bool IsDirectoryCanBeOpened(const std::string& vName) override {
632
0
        if (!vName.empty()) {
633
0
            DIR* pDir = nullptr;
634
            // interesting, in the case of a protected dir or for any reason the dir cant be opened
635
            // this func will fail
636
0
            pDir = opendir(vName.c_str());
637
0
            if (pDir != nullptr) {
638
0
                (void)closedir(pDir);
639
0
                return true;
640
0
            }
641
0
        }
642
0
        return false;
643
0
    }
644
0
    bool IsDirectoryExist(const std::string& vName) override {
645
0
        bool bExists = false;
646
0
        if (!vName.empty()) {
647
0
            DIR* pDir = nullptr;
648
0
            pDir      = opendir(vName.c_str());
649
0
            if (pDir) {
650
0
                bExists = true;
651
0
                closedir(pDir);
652
0
            } else if (ENOENT == errno) {
653
                /* Directory does not exist. */
654
                // bExists = false;
655
0
            } else {
656
                /* opendir() failed for some other reason.
657
                   like if a dir is protected, or not accessable with user right
658
                */
659
0
                bExists = true;
660
0
            }
661
0
        }
662
0
        return bExists;
663
0
    }
664
0
    bool IsFileExist(const std::string& vName) override {
665
0
        std::ifstream docFile(vName, std::ios::in);
666
0
        if (docFile.is_open()) {
667
0
            docFile.close();
668
0
            return true;
669
0
        }
670
0
        return false;
671
0
    }
672
0
    bool CreateDirectoryIfNotExist(const std::string& vName) override {
673
0
        bool res = false;
674
0
        if (!vName.empty()) {
675
0
            if (!IsDirectoryExist(vName)) {
676
#ifdef _IGFD_WIN_
677
                std::wstring wname = IGFD::Utils::UTF8Decode(vName);
678
                if (CreateDirectoryW(wname.c_str(), nullptr)) {
679
                    res = true;
680
                }
681
#elif defined(__EMSCRIPTEN__)  // _IGFD_WIN_
682
                std::string str = std::string("FS.mkdir('") + vName + "');";
683
                emscripten_run_script(str.c_str());
684
                res = true;
685
#elif defined(_IGFD_UNIX_)
686
                char buffer[PATH_MAX] = {};
687
0
                snprintf(buffer, PATH_MAX, "mkdir -p \"%s\"", vName.c_str());
688
0
                const int dir_err = std::system(buffer);
689
0
                if (dir_err != -1) {
690
0
                    res = true;
691
0
                }
692
0
#endif  // _IGFD_WIN_
693
0
                if (!res) {
694
0
                    std::cout << "Error creating directory " << vName << std::endl;
695
0
                }
696
0
            }
697
0
        }
698
699
0
        return res;
700
0
    }
701
702
0
    std::vector<IGFD::PathDisplayedName> GetDevicesList() override {
703
0
        std::vector<IGFD::PathDisplayedName> res;
704
#ifdef _IGFD_WIN_
705
        const DWORD mydevices = 2048;
706
        char lpBuffer[2048];
707
#define mini(a, b) (((a) < (b)) ? (a) : (b))
708
        const DWORD countChars = mini(GetLogicalDriveStringsA(mydevices, lpBuffer), 2047);
709
#undef mini
710
        if (countChars > 0U && countChars < 2049U) {
711
            std::string var = std::string(lpBuffer, (size_t)countChars);
712
            IGFD::Utils::ReplaceString(var, "\\", "");
713
            auto arr = IGFD::Utils::SplitStringToVector(var, '\0', false);
714
            wchar_t szVolumeName[2048];
715
            IGFD::PathDisplayedName path_name;
716
            for (auto& a : arr) {
717
                path_name.first = a;
718
                path_name.second.clear();
719
                std::wstring wpath = IGFD::Utils::UTF8Decode(a);
720
                if (GetVolumeInformationW(wpath.c_str(), szVolumeName, 2048, nullptr, nullptr, nullptr, nullptr, 0)) {
721
                    path_name.second = IGFD::Utils::UTF8Encode(szVolumeName);
722
                    res.push_back(path_name);
723
                }
724
            }
725
        }
726
#endif  // _IGFD_WIN_
727
0
        return res;
728
0
    }
729
730
0
    IGFD::Utils::PathStruct ParsePathFileName(const std::string& vPathFileName) override {
731
0
        IGFD::Utils::PathStruct res;
732
0
        if (!vPathFileName.empty()) {
733
0
            std::string pfn = vPathFileName;
734
0
            std::string separator(1u, PATH_SEP);
735
0
            IGFD::Utils::ReplaceString(pfn, "\\", separator);
736
0
            IGFD::Utils::ReplaceString(pfn, "/", separator);
737
0
            size_t lastSlash = pfn.find_last_of(separator);
738
0
            if (lastSlash != std::string::npos) {
739
0
                res.name = pfn.substr(lastSlash + 1);
740
0
                res.path = pfn.substr(0, lastSlash);
741
0
                res.isOk = true;
742
0
            }
743
0
            size_t lastPoint = pfn.find_last_of('.');
744
0
            if (lastPoint != std::string::npos) {
745
0
                if (!res.isOk) {
746
0
                    res.name = pfn;
747
0
                    res.isOk = true;
748
0
                }
749
0
                res.ext = pfn.substr(lastPoint + 1);
750
0
                IGFD::Utils::ReplaceString(res.name, "." + res.ext, "");
751
0
            }
752
0
            if (!res.isOk) {
753
0
                res.name = std::move(pfn);
754
0
                res.isOk = true;
755
0
            }
756
0
        }
757
0
        return res;
758
0
    }
759
760
0
    std::vector<IGFD::FileInfos> ScanDirectory(const std::string& vPath) override {
761
0
        std::vector<IGFD::FileInfos> res;
762
0
        struct dirent** files = nullptr;
763
0
        size_t n              = scandir(vPath.c_str(), &files, nullptr,                         //
764
0
                                        [](const struct dirent** a, const struct dirent** b) {  //
765
0
                               return strcoll((*a)->d_name, (*b)->d_name);
766
0
                           });
767
0
        if (n && files) {
768
0
            for (size_t i = 0; i < n; ++i) {
769
0
                struct dirent* ent = files[i];
770
0
                IGFD::FileType fileType;
771
0
                switch (ent->d_type) {
772
0
                    case DT_DIR: fileType.SetContent(IGFD::FileType::ContentType::Directory); break;
773
0
                    case DT_REG: fileType.SetContent(IGFD::FileType::ContentType::File); break;
774
0
#if defined(_IGFD_UNIX_) || (DT_LNK != DT_UNKNOWN)
775
0
                    case DT_LNK:
776
0
#endif
777
0
                    case DT_UNKNOWN: {
778
0
                        struct stat sb = {};
779
#ifdef _IGFD_WIN_
780
                        const auto wfpn = IGFD::Utils::UTF8Decode(vPath + ent->d_name);
781
                        if (!_wstati64(wfpn.c_str(), &sb)) {
782
#else
783
0
                        const auto fpn = vPath + IGFD::Utils::GetPathSeparator() + ent->d_name;
784
0
                        if (!stat(fpn.c_str(), &sb)) {
785
0
#endif
786
0
                            if (sb.st_mode & S_IFLNK) {
787
0
                                fileType.SetSymLink(true);
788
                                // by default if we can't figure out the target type.
789
0
                                fileType.SetContent(IGFD::FileType::ContentType::LinkToUnknown);
790
0
                            }
791
0
                            if (sb.st_mode & S_IFREG) {
792
0
                                fileType.SetContent(IGFD::FileType::ContentType::File);
793
0
                                break;
794
0
                            } else if (sb.st_mode & S_IFDIR) {
795
0
                                fileType.SetContent(IGFD::FileType::ContentType::Directory);
796
0
                                break;
797
0
                            }
798
0
                        }
799
0
                        break;
800
0
                    }
801
0
                    default: break;  // leave it invalid (devices, etc.)
802
0
                }
803
0
                if (fileType.isValid()) {
804
0
                    IGFD::FileInfos _file;
805
0
                    _file.filePath    = vPath;
806
0
                    _file.fileNameExt = ent->d_name;
807
0
                    _file.fileType    = fileType;
808
0
                    res.push_back(_file);
809
0
                }
810
0
            }
811
0
            for (size_t i = 0; i < n; ++i) {
812
0
                free(files[i]);
813
0
            }
814
0
            free(files);
815
0
        }
816
0
        return res;
817
0
    }
818
0
    bool IsDirectory(const std::string& vFilePathName) override {
819
0
        DIR* pDir = opendir(vFilePathName.c_str());
820
0
        if (pDir) {
821
0
            (void)closedir(pDir);
822
0
            return true;
823
0
        }
824
0
        return false;
825
0
    }
826
0
    void GetFileDateAndSize(const std::string& vFilePathName, const IGFD::FileType& vFileType, std::string& voDate, size_t& voSize) override {
827
0
        struct stat statInfos{};
828
0
        int32_t result{};
829
#ifdef _IGFD_WIN_
830
        std::wstring wfpn = IGFD::Utils::UTF8Decode(vFilePathName);
831
        result            = _wstati64(wfpn.c_str(), &statInfos);
832
#else
833
0
        result = stat(vFilePathName.c_str(), &statInfos);
834
0
#endif
835
0
        static char timebuf[100];
836
0
        if (!result) {
837
            // date
838
0
            size_t len = 0;
839
#ifdef _MSC_VER
840
            struct tm _tm;
841
            errno_t err = localtime_s(&_tm, &statInfos.st_mtime);
842
            if (!err) len = strftime(timebuf, 99, DateTimeFormat, &_tm);
843
#else   // _MSC_VER
844
0
            struct tm* _tm = localtime(&statInfos.st_mtime);
845
0
            if (_tm) len = strftime(timebuf, 99, DateTimeFormat, _tm);
846
0
#endif  // _MSC_VER
847
0
            if (len) {
848
0
                voDate = std::string(timebuf, len);
849
0
            }
850
            // size
851
0
            if (!vFileType.isDir()) {
852
0
                voSize = (size_t)statInfos.st_size;
853
0
            }
854
0
        }
855
0
    }
856
};
857
1
#define FILE_SYSTEM_OVERRIDE FileSystemDirent
858
#endif  // USE_STD_FILESYSTEM
859
#else
860
#include CUSTOM_FILESYSTEM_INCLUDE
861
#endif  // USE_CUSTOM_FILESYSTEM
862
863
// https://github.com/ocornut/imgui/issues/1720
864
0
bool IGFD::Utils::ImSplitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size) {
865
0
    auto* window = ImGui::GetCurrentWindow();
866
0
    ImGuiID id   = window->GetID("##Splitter");
867
0
    ImRect bb;
868
0
    bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1));
869
0
    bb.Max = bb.Min + ImGui::CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f);
870
0
    return ImGui::SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 1.0f, 0.0, ImGui::GetColorU32(ImGuiCol_FrameBg));
871
0
}
872
873
// Convert a wide Unicode string to an UTF8 string
874
0
std::string IGFD::Utils::UTF8Encode(const std::wstring& wstr) {
875
0
    std::string res;
876
#ifdef _IGFD_WIN_
877
    if (!wstr.empty()) {
878
        int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), nullptr, 0, nullptr, nullptr);
879
        if (size_needed) {
880
            res = std::string(size_needed, 0);
881
            WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &res[0], size_needed, nullptr, nullptr);
882
        }
883
    }
884
#else
885
    // Suppress warnings from the compiler.
886
0
    (void)wstr;
887
0
#endif  // _IGFD_WIN_
888
0
    return res;
889
0
}
890
891
// Convert an UTF8 string to a wide Unicode String
892
0
std::wstring IGFD::Utils::UTF8Decode(const std::string& str) {
893
0
    std::wstring res;
894
#ifdef _IGFD_WIN_
895
    if (!str.empty()) {
896
        int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), nullptr, 0);
897
        if (size_needed) {
898
            res = std::wstring(size_needed, 0);
899
            MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &res[0], size_needed);
900
        }
901
    }
902
#else
903
    // Suppress warnings from the compiler.
904
0
    (void)str;
905
0
#endif  // _IGFD_WIN_
906
0
    return res;
907
0
}
908
909
0
bool IGFD::Utils::ReplaceString(std::string& str, const ::std::string& oldStr, const ::std::string& newStr, const size_t& vMaxRecursion) {
910
0
    if (!str.empty() && oldStr != newStr) {
911
0
        bool res             = false;
912
0
        size_t pos           = 0;
913
0
        bool found           = false;
914
0
        size_t max_recursion = vMaxRecursion;
915
0
        do {
916
0
            pos = str.find(oldStr, pos);
917
0
            if (pos != std::string::npos) {
918
0
                found = res = true;
919
0
                str.replace(pos, oldStr.length(), newStr);
920
0
                pos += newStr.length();
921
0
            } else if (found && max_recursion > 0) {  // recursion loop
922
0
                found = false;
923
0
                pos   = 0;
924
0
                --max_recursion;
925
0
            }
926
0
        } while (pos != std::string::npos);
927
0
        return res;
928
0
    }
929
0
    return false;
930
0
}
931
932
0
std::vector<std::string> IGFD::Utils::SplitStringToVector(const std::string& vText, const std::string& vDelimiterPattern, const bool vPushEmpty) {
933
0
    std::vector<std::string> arr;
934
0
    if (!vText.empty()) {
935
0
        size_t start = 0;
936
0
        size_t end   = vText.find(vDelimiterPattern, start);
937
0
        while (end != std::string::npos) {
938
0
            auto token = vText.substr(start, end - start);
939
0
            if (!token.empty() || (token.empty() && vPushEmpty)) {  //-V728
940
0
                arr.push_back(token);
941
0
            }
942
0
            start = end + vDelimiterPattern.size();
943
0
            end   = vText.find(vDelimiterPattern, start);
944
0
        }
945
0
        auto token = vText.substr(start);
946
0
        if (!token.empty() || (token.empty() && vPushEmpty)) {  //-V728
947
0
            arr.push_back(token);
948
0
        }
949
0
    }
950
0
    return arr;
951
0
}
952
953
0
std::vector<std::string> IGFD::Utils::SplitStringToVector(const std::string& vText, const char& vDelimiter, const bool vPushEmpty) {
954
0
    std::vector<std::string> arr;
955
0
    if (!vText.empty()) {
956
0
        size_t start = 0;
957
0
        size_t end   = vText.find(vDelimiter, start);
958
0
        while (end != std::string::npos) {
959
0
            auto token = vText.substr(start, end - start);
960
0
            if (!token.empty() || (token.empty() && vPushEmpty)) {  //-V728
961
0
                arr.push_back(token);
962
0
            }
963
0
            start = end + 1;
964
0
            end   = vText.find(vDelimiter, start);
965
0
        }
966
0
        auto token = vText.substr(start);
967
0
        if (!token.empty() || (token.empty() && vPushEmpty)) {  //-V728
968
0
            arr.push_back(token);
969
0
        }
970
0
    }
971
0
    return arr;
972
0
}
973
974
0
void IGFD::Utils::AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) {
975
0
    std::string st = vStr;
976
0
    size_t len     = vBufferLen - 1u;
977
0
    size_t slen    = strlen(vBuffer);
978
979
0
    if (!st.empty() && st != "\n") {
980
0
        IGFD::Utils::ReplaceString(st, "\n", "");
981
0
        IGFD::Utils::ReplaceString(st, "\r", "");
982
0
    }
983
0
    vBuffer[slen]   = '\0';
984
0
    std::string str = std::string(vBuffer);
985
    // if (!str.empty()) str += "\n";
986
0
    str += vStr;
987
0
    if (len > str.size()) {
988
0
        len = str.size();
989
0
    }
990
#ifdef _MSC_VER
991
    strncpy_s(vBuffer, vBufferLen, str.c_str(), len);
992
#else   // _MSC_VER
993
0
    strncpy(vBuffer, str.c_str(), len);
994
0
#endif  // _MSC_VER
995
0
    vBuffer[len] = '\0';
996
0
}
997
998
0
void IGFD::Utils::ResetBuffer(char* vBuffer) {
999
0
    vBuffer[0] = '\0';
1000
0
}
1001
1002
0
void IGFD::Utils::SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr) {
1003
0
    ResetBuffer(vBuffer);
1004
0
    AppendToBuffer(vBuffer, vBufferLen, vStr);
1005
0
}
1006
1007
0
std::string IGFD::Utils::LowerCaseString(const std::string& vString) {
1008
0
    auto str = vString;
1009
1010
    // convert to lower case
1011
0
    for (char& c : str) {
1012
0
        c = (char)std::tolower(c);
1013
0
    }
1014
1015
0
    return str;
1016
0
}
1017
1018
0
size_t IGFD::Utils::GetCharCountInString(const std::string& vString, const char& vChar) {
1019
0
    size_t res = 0U;
1020
0
    for (const auto& c : vString) {
1021
0
        if (c == vChar) {
1022
0
            ++res;
1023
0
        }
1024
0
    }
1025
0
    return res;
1026
0
}
1027
1028
0
size_t IGFD::Utils::GetLastCharPosWithMinCharCount(const std::string& vString, const char& vChar, const size_t& vMinCharCount) {
1029
0
    if (vMinCharCount) {
1030
0
        size_t last_dot_pos = vString.size() + 1U;
1031
0
        size_t count_dots   = vMinCharCount;
1032
0
        while (count_dots > 0U && last_dot_pos > 0U && last_dot_pos != std::string::npos) {
1033
0
            auto new_dot = vString.rfind(vChar, last_dot_pos - 1U);
1034
0
            if (new_dot != std::string::npos) {
1035
0
                last_dot_pos = new_dot;
1036
0
                --count_dots;
1037
0
            } else {
1038
0
                break;
1039
0
            }
1040
0
        }
1041
0
        return last_dot_pos;
1042
0
    }
1043
0
    return std::string::npos;
1044
0
}
1045
1046
1
std::string IGFD::Utils::GetPathSeparator() {
1047
1
    return std::string(1U, PATH_SEP);
1048
1
}
1049
1050
0
std::string IGFD::Utils::RoundNumber(double vvalue, int n) {
1051
0
    std::stringstream tmp;
1052
0
    tmp << std::setprecision(n) << std::fixed << vvalue;
1053
0
    return tmp.str();
1054
0
}
1055
1056
0
std::pair<std::string, std::string> IGFD::Utils::FormatFileSize(size_t vByteSize) {
1057
0
    if (vByteSize != 0) {
1058
0
        static auto lo = 1024.0;
1059
0
        static auto ko = 1024.0 * 1024.0;
1060
0
        static auto mo = 1024.0 * 1024.0 * 1024.0;
1061
0
        const auto v   = static_cast<double>(vByteSize);
1062
0
        if (v < lo) {
1063
0
            return {RoundNumber(v, 0), fileSizeBytes};  // octet
1064
0
        } else if (v < ko) {
1065
0
            return {RoundNumber(v / lo, 2), fileSizeKiloBytes};  // ko
1066
0
        } else if (v < mo) {
1067
0
            return {RoundNumber(v / ko, 2), fileSizeMegaBytes};  // Mo
1068
0
        } else {
1069
0
            return {RoundNumber(v / mo, 2), fileSizeGigaBytes};  // Go
1070
0
        }
1071
0
    }
1072
0
    return {"0", fileSizeBytes};
1073
0
}
1074
1075
// https://cplusplus.com/reference/cstdlib/strtod
1076
0
bool IGFD::Utils::M_IsAValidCharExt(const char& c) {
1077
0
    return c == '.' ||            // .5
1078
0
           c == '-' || c == '+';  // -2.5 or +2.5;
1079
0
}
1080
1081
// https://cplusplus.com/reference/cstdlib/strtod
1082
0
bool IGFD::Utils::M_IsAValidCharSuffix(const char& c) {
1083
0
    return c == 'e' || c == 'E' ||  // 1e5 or 1E5
1084
0
           c == 'x' || c == 'X' ||  // 0x14 or 0X14
1085
0
           c == 'p' || c == 'P';    // 6.2p2 or 3.2P-5
1086
0
}
1087
1088
0
bool IGFD::Utils::M_ExtractNumFromStringAtPos(const std::string& str, size_t& pos, double& vOutNum) {
1089
0
    if (!str.empty() && pos < str.size()) {
1090
0
        const char fc = str.at(pos);  // first char
1091
        // if the first char is not possible for a number we quit
1092
0
        if (std::isdigit(fc) || M_IsAValidCharExt(fc)) {
1093
0
            static constexpr size_t COUNT_CHAR = 64;
1094
0
            char buf[COUNT_CHAR + 1];
1095
0
            size_t buf_p        = 0;
1096
0
            bool is_last_digit  = false;
1097
0
            bool is_last_suffix = false;
1098
0
            const auto& ss      = str.size();
1099
0
            while (ss > 1 && pos < ss && buf_p < COUNT_CHAR) {
1100
0
                const char& c = str.at(pos);
1101
                // a suffix must be after a number
1102
0
                if (is_last_digit && M_IsAValidCharSuffix(c)) {
1103
0
                    is_last_suffix = true;
1104
0
                    buf[buf_p++]   = c;
1105
0
                } else if (std::isdigit(c)) {
1106
0
                    is_last_suffix = false;
1107
0
                    is_last_digit  = true;
1108
0
                    buf[buf_p++]   = c;
1109
0
                } else if (M_IsAValidCharExt(c)) {
1110
0
                    is_last_digit = false;
1111
0
                    buf[buf_p++]  = c;
1112
0
                } else {
1113
0
                    break;
1114
0
                }
1115
0
                ++pos;
1116
0
            }
1117
            // if the last char is a suffix so its not a number
1118
0
            if (buf_p != 0 && !is_last_suffix) {
1119
0
                buf[buf_p] = '\0';
1120
0
                char* endPtr;
1121
0
                vOutNum = strtod(buf, &endPtr);
1122
                // the edge cases for numbers will be next filtered by strtod
1123
0
                if (endPtr != buf) {
1124
0
                    return true;
1125
0
                }
1126
0
            }
1127
0
        }
1128
0
    }
1129
0
    return false;
1130
0
}
1131
1132
// Fonction de comparaison naturelle entre deux cha�nes
1133
0
bool IGFD::Utils::NaturalCompare(const std::string& vA, const std::string& vB, bool vInsensitiveCase, bool vDescending) {
1134
0
    std::size_t ia = 0, ib = 0;
1135
0
    double nA, nB;
1136
0
    const auto& as = vA.size();
1137
0
    const auto& bs = vB.size();
1138
0
    while (ia < as && ib < bs) {
1139
0
        const char& ca = vInsensitiveCase ? std::tolower(vA[ia]) : vA[ia];
1140
0
        const char& cb = vInsensitiveCase ? std::tolower(vB[ib]) : vB[ib];
1141
        // we cannot start a number extraction from suffixs
1142
0
        const auto rA = M_ExtractNumFromStringAtPos(vA, ia, nA);
1143
0
        const auto rB = M_ExtractNumFromStringAtPos(vB, ib, nB);
1144
0
        if (rA && rB) {
1145
0
            if (nA != nB) {
1146
0
                return vDescending ? nA > nB : nA < nB;
1147
0
            }
1148
0
        } else {
1149
0
            if (ca != cb) {
1150
0
                return vDescending ? ca > cb : ca < cb;
1151
0
            }
1152
0
            ++ia;
1153
0
            ++ib;
1154
0
        }
1155
0
    }
1156
0
    return vDescending ? as > bs : as < bs;  // toto1 < toto1+
1157
0
}
1158
1159
0
IGFD::FileStyle::FileStyle() : color(0, 0, 0, 0) {
1160
0
}
1161
1162
0
IGFD::FileStyle::FileStyle(const FileStyle& vStyle) {
1163
0
    color = vStyle.color;
1164
0
    icon  = vStyle.icon;
1165
0
    font  = vStyle.font;
1166
0
    flags = vStyle.flags;
1167
0
}
1168
1169
0
IGFD::FileStyle::FileStyle(const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) : color(vColor), icon(vIcon), font(vFont) {
1170
0
}
1171
1172
0
void IGFD::SearchManager::Clear() {
1173
0
    searchTag.clear();
1174
0
    IGFD::Utils::ResetBuffer(searchBuffer);
1175
0
}
1176
1177
0
void IGFD::SearchManager::DrawSearchBar(FileDialogInternal& vFileDialogInternal) {
1178
    // search field
1179
0
    if (IMGUI_BUTTON(resetButtonString "##BtnImGuiFileDialogSearchField")) {
1180
0
        Clear();
1181
0
        vFileDialogInternal.fileManager.ApplyFilteringOnFileList(vFileDialogInternal);
1182
0
    }
1183
0
    if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonResetSearchString);
1184
0
    ImGui::SameLine();
1185
0
    ImGui::Text(searchString);
1186
0
    ImGui::SameLine();
1187
0
    ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
1188
0
    bool edited = ImGui::InputText("##InputImGuiFileDialogSearchField", searchBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
1189
0
    if (ImGui::GetItemID() == ImGui::GetActiveID()) searchInputIsActive = true;
1190
0
    ImGui::PopItemWidth();
1191
0
    if (edited) {
1192
0
        searchTag = searchBuffer;
1193
0
        vFileDialogInternal.fileManager.ApplyFilteringOnFileList(vFileDialogInternal);
1194
0
    }
1195
0
}
1196
1197
0
void IGFD::FilterInfos::setCollectionTitle(const std::string& vTitle) {
1198
0
    title = vTitle;
1199
0
}
1200
1201
0
void IGFD::FilterInfos::addFilter(const std::string& vFilter, const bool vIsRegex) {
1202
0
    setCollectionTitle(vFilter);
1203
0
    addCollectionFilter(vFilter, vIsRegex);
1204
0
}
1205
1206
0
void IGFD::FilterInfos::addCollectionFilter(const std::string& vFilter, const bool vIsRegex) {
1207
0
    if (!vIsRegex) {
1208
0
        auto _count_dots = Utils::GetCharCountInString(vFilter, '.');
1209
0
        if (_count_dots > IGFD::FilterInfos::count_dots) {
1210
0
            IGFD::FilterInfos::count_dots = _count_dots;
1211
0
        }
1212
0
        if (vFilter.find('*') != std::string::npos) {
1213
0
            const auto& regex_string = transformAsteriskBasedFilterToRegex(vFilter);
1214
0
            addCollectionFilter(regex_string, true);
1215
0
            return;
1216
0
        }
1217
0
        filters.try_add(vFilter);
1218
0
        filters_optimized.try_add(Utils::LowerCaseString(vFilter));
1219
0
    } else {
1220
0
        try {
1221
0
            auto rx = std::regex(vFilter);
1222
0
            filters.try_add(vFilter);
1223
0
            filters_regex.emplace_back(rx);
1224
0
        } catch (std::exception& e) {
1225
0
            const std::string msg = "IGFD : The regex \"" + vFilter + "\" parsing was failed with msg : " + e.what();
1226
0
            throw IGFDException(msg.c_str());
1227
0
        }
1228
0
    }
1229
0
}
1230
1231
0
void IGFD::FilterInfos::clear() {
1232
0
    title.clear();
1233
0
    filters.clear();
1234
0
    filters_optimized.clear();
1235
0
    filters_regex.clear();
1236
0
}
1237
1238
0
bool IGFD::FilterInfos::empty() const {
1239
0
    return filters.empty() || filters.begin()->empty();
1240
0
}
1241
1242
0
const std::string& IGFD::FilterInfos::getFirstFilter() const {
1243
0
    if (!filters.empty()) {
1244
0
        return *filters.begin();
1245
0
    }
1246
0
    return empty_string;
1247
0
}
1248
1249
0
bool IGFD::FilterInfos::exist(const FileInfos& vFileInfos, bool vIsCaseInsensitive) const {
1250
0
    for (const auto& filter : filters) {
1251
0
        if (vFileInfos.SearchForExt(filter, vIsCaseInsensitive, count_dots)) {
1252
0
            return true;
1253
0
        }
1254
0
    }
1255
0
    return false;
1256
0
}
1257
1258
0
bool IGFD::FilterInfos::regexExist(const std::string& vFilter) const {
1259
0
    for (const auto& regex : filters_regex) {
1260
0
        if (std::regex_search(vFilter, regex)) {
1261
0
            return true;
1262
0
        }
1263
0
    }
1264
0
    return false;
1265
0
}
1266
1267
0
std::string IGFD::FilterInfos::transformAsteriskBasedFilterToRegex(const std::string& vFilter) {
1268
0
    std::string res;
1269
0
    if (!vFilter.empty() && vFilter.find('*') != std::string::npos) {
1270
0
        res = "((";
1271
0
        for (const auto& c : vFilter) {
1272
0
            if (c == '.') {
1273
0
                res += "[.]";  // [.] => a dot
1274
0
            } else if (c == '*') {
1275
0
                res += ".*";  // .* => any char zero or many
1276
0
            } else {
1277
0
                res += c;  // other chars
1278
0
            }
1279
0
        }
1280
0
        res += "$))";  // $ => end fo the string
1281
0
    }
1282
0
    return res;
1283
0
}
1284
1285
0
const IGFD::FilterInfos& IGFD::FilterManager::GetSelectedFilter() const {
1286
0
    return m_SelectedFilter;
1287
0
}
1288
1289
0
void IGFD::FilterManager::ParseFilters(const char* vFilters) {
1290
0
    m_ParsedFilters.clear();
1291
1292
0
    if (vFilters) {
1293
0
        dLGFilters = vFilters;  // file mode
1294
0
    } else {
1295
0
        dLGFilters.clear();  // directory mode
1296
0
    }
1297
1298
0
    if (!dLGFilters.empty()) {
1299
        /* Rules
1300
        0) a filter must have 2 chars mini and the first must be a .
1301
        1) a regex must be in (( and ))
1302
        2) a , will separate filters except if between a ( and )
1303
        3) name{filter1, filter2} is a spetial form for collection filters
1304
        3.1) the name can be composed of what you want except { and }
1305
        3.2) the filter can be a regex
1306
        4) the filters cannot integrate these chars '(' ')' '{' '}' ' ' except for a regex with respect to rule 1)
1307
        5) the filters cannot integrate a ','
1308
        */
1309
1310
0
        bool current_filter_found = false;
1311
0
        bool started              = false;
1312
0
        bool regex_started        = false;
1313
0
        bool parenthesis_started  = false;
1314
1315
0
        std::string word;
1316
0
        std::string filter_name;
1317
1318
0
        char last_char = 0;
1319
0
        for (char c : dLGFilters) {
1320
0
            if (c == '{') {
1321
0
                if (regex_started) {
1322
0
                    word += c;
1323
0
                } else {
1324
0
                    started = true;
1325
0
                    m_ParsedFilters.emplace_back();
1326
0
                    m_ParsedFilters.back().setCollectionTitle(filter_name);
1327
0
                    filter_name.clear();
1328
0
                    word.clear();
1329
0
                }
1330
0
            } else if (c == '}') {
1331
0
                if (regex_started) {
1332
0
                    word += c;
1333
0
                } else {
1334
0
                    if (started) {
1335
0
                        if (word.size() > 1U && word[0] == '.') {
1336
0
                            if (m_ParsedFilters.empty()) {
1337
0
                                m_ParsedFilters.emplace_back();
1338
0
                            }
1339
0
                            m_ParsedFilters.back().addCollectionFilter(word, false);
1340
0
                        }
1341
0
                        word.clear();
1342
0
                        filter_name.clear();
1343
0
                        started = false;
1344
0
                    }
1345
0
                }
1346
0
            } else if (c == '(') {
1347
0
                word += c;
1348
0
                if (last_char == '(') {
1349
0
                    regex_started = true;
1350
0
                }
1351
0
                parenthesis_started = true;
1352
0
                if (!started) {
1353
0
                    filter_name += c;
1354
0
                }
1355
0
            } else if (c == ')') {
1356
0
                word += c;
1357
0
                if (last_char == ')') {
1358
0
                    if (regex_started) {
1359
0
                        if (started) {
1360
0
                            m_ParsedFilters.back().addCollectionFilter(word, true);
1361
0
                        } else {
1362
0
                            m_ParsedFilters.emplace_back();
1363
0
                            m_ParsedFilters.back().addFilter(word, true);
1364
0
                        }
1365
0
                        word.clear();
1366
0
                        filter_name.clear();
1367
0
                        regex_started = false;
1368
0
                    } else {
1369
0
                        if (!started) {
1370
0
                            if (!m_ParsedFilters.empty()) {
1371
0
                                m_ParsedFilters.erase(m_ParsedFilters.begin() + m_ParsedFilters.size() - 1U);
1372
0
                            } else {
1373
0
                                m_ParsedFilters.clear();
1374
0
                            }
1375
0
                        }
1376
0
                        word.clear();
1377
0
                        filter_name.clear();
1378
0
                    }
1379
0
                }
1380
0
                parenthesis_started = false;
1381
0
                if (!started) {
1382
0
                    filter_name += c;
1383
0
                }
1384
0
            } else if (c == '.') {
1385
0
                word += c;
1386
0
                if (!started) {
1387
0
                    filter_name += c;
1388
0
                }
1389
0
            } else if (c == ',') {
1390
0
                if (regex_started) {
1391
0
                    word += c;
1392
0
                } else {
1393
0
                    if (started) {
1394
0
                        if (word.size() > 1U && word[0] == '.') {
1395
0
                            m_ParsedFilters.back().addCollectionFilter(word, false);
1396
0
                            word.clear();
1397
0
                            filter_name.clear();
1398
0
                        }
1399
0
                    } else {
1400
0
                        if (word.size() > 1U && word[0] == '.') {
1401
0
                            m_ParsedFilters.emplace_back();
1402
0
                            m_ParsedFilters.back().addFilter(word, false);
1403
0
                            word.clear();
1404
0
                            filter_name.clear();
1405
0
                        }
1406
0
                        if (parenthesis_started) {
1407
0
                            filter_name += c;
1408
0
                        }
1409
0
                    }
1410
0
                }
1411
0
            } else {
1412
0
                if (c != ' ') {
1413
0
                    word += c;
1414
0
                }
1415
0
                if (!started) {
1416
0
                    filter_name += c;
1417
0
                }
1418
0
            }
1419
0
            last_char = c;
1420
0
        }
1421
1422
0
        if (started) {
1423
0
            if (!m_ParsedFilters.empty()) {
1424
0
                m_ParsedFilters.erase(m_ParsedFilters.begin() + m_ParsedFilters.size() - 1U);
1425
0
            } else {
1426
0
                m_ParsedFilters.clear();
1427
0
            }
1428
0
        } else if (word.size() > 1U && word[0] == '.') {
1429
0
            m_ParsedFilters.emplace_back();
1430
0
            m_ParsedFilters.back().addFilter(word, false);
1431
0
            word.clear();
1432
0
        }
1433
1434
0
        for (const auto& it : m_ParsedFilters) {
1435
0
            if (it.title == m_SelectedFilter.title) {
1436
0
                m_SelectedFilter     = it;
1437
0
                current_filter_found = true;
1438
0
                break;
1439
0
            }
1440
0
        }
1441
1442
0
        if (!current_filter_found) {
1443
0
            if (!m_ParsedFilters.empty()) {
1444
0
                m_SelectedFilter = *m_ParsedFilters.begin();
1445
0
            }
1446
0
        }
1447
0
    }
1448
0
}
1449
1450
0
void IGFD::FilterManager::SetSelectedFilterWithExt(const std::string& vFilter) {
1451
0
    if (!m_ParsedFilters.empty()) {
1452
0
        if (!vFilter.empty()) {
1453
0
            for (const auto& infos : m_ParsedFilters) {
1454
0
                for (const auto& filter : infos.filters) {
1455
0
                    if (vFilter == filter) {
1456
0
                        m_SelectedFilter = infos;
1457
0
                    }
1458
0
                }
1459
0
            }
1460
0
        }
1461
1462
0
        if (m_SelectedFilter.empty()) {
1463
0
            m_SelectedFilter = *m_ParsedFilters.begin();
1464
0
        }
1465
0
    }
1466
0
}
1467
1468
0
void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) {
1469
0
    std::string _criteria                  = (vCriteria != nullptr) ? std::string(vCriteria) : "";
1470
0
    m_FilesStyle[vFlags][_criteria]        = std::make_shared<FileStyle>(vInfos);
1471
0
    m_FilesStyle[vFlags][_criteria]->flags = vFlags;
1472
0
}
1473
1474
// will be called internally
1475
// will not been exposed to IGFD API
1476
0
bool IGFD::FilterManager::FillFileStyle(std::shared_ptr<FileInfos> vFileInfos) const {
1477
    // todo : better system to found regarding what style to priorize regarding other
1478
    // maybe with a lambda fucntion for let the user use his style
1479
    // according to his use case
1480
0
    if (vFileInfos.use_count() && !m_FilesStyle.empty()) {
1481
0
        for (const auto& _flag : m_FilesStyle) {
1482
0
            for (const auto& _file : _flag.second) {
1483
0
                if ((_flag.first & IGFD_FileStyleByTypeDir && _flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType.isDir() && vFileInfos->fileType.isSymLink()) ||
1484
0
                    (_flag.first & IGFD_FileStyleByTypeFile && _flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType.isFile() && vFileInfos->fileType.isSymLink()) ||
1485
0
                    (_flag.first & IGFD_FileStyleByTypeLink && vFileInfos->fileType.isSymLink()) || (_flag.first & IGFD_FileStyleByTypeDir && vFileInfos->fileType.isDir()) ||
1486
0
                    (_flag.first & IGFD_FileStyleByTypeFile && vFileInfos->fileType.isFile())) {
1487
0
                    if (_file.first.empty()) {  // for all links
1488
0
                        vFileInfos->fileStyle = _file.second;
1489
0
                    } else if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileNameExt,
1490
0
                                                                                                std::regex(_file.first))) {  // for links who are equal to style criteria
1491
0
                        vFileInfos->fileStyle = _file.second;
1492
0
                    } else if (_file.first == vFileInfos->fileNameExt) {  // for links who are equal to style criteria
1493
0
                        vFileInfos->fileStyle = _file.second;
1494
0
                    }
1495
0
                }
1496
1497
0
                if (_flag.first & IGFD_FileStyleByExtention) {
1498
0
                    if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileExtLevels[0], std::regex(_file.first))) {
1499
0
                        vFileInfos->fileStyle = _file.second;
1500
0
                    } else if (vFileInfos->SearchForExt(_file.first, false)) {
1501
0
                        vFileInfos->fileStyle = _file.second;
1502
0
                    }
1503
0
                }
1504
1505
0
                if (_flag.first & IGFD_FileStyleByFullName) {
1506
0
                    if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileNameExt, std::regex(_file.first))) {
1507
0
                        vFileInfos->fileStyle = _file.second;
1508
0
                    } else if (_file.first == vFileInfos->fileNameExt) {
1509
0
                        vFileInfos->fileStyle = _file.second;
1510
0
                    }
1511
0
                }
1512
1513
0
                if (_flag.first & IGFD_FileStyleByContainedInFullName) {
1514
0
                    if (_file.first.find("((") != std::string::npos && std::regex_search(vFileInfos->fileNameExt, std::regex(_file.first))) {
1515
0
                        vFileInfos->fileStyle = _file.second;
1516
0
                    } else if (vFileInfos->fileNameExt.find(_file.first) != std::string::npos) {
1517
0
                        vFileInfos->fileStyle = _file.second;
1518
0
                    }
1519
0
                }
1520
1521
0
                for (auto& functor : m_FilesStyleFunctors) {
1522
0
                    if (functor) {
1523
0
                        FileStyle result;
1524
0
                        if (functor(*(vFileInfos.get()), result)) {
1525
0
                            vFileInfos->fileStyle = std::make_shared<FileStyle>(std::move(result));
1526
0
                        }
1527
0
                    }
1528
0
                }
1529
1530
0
                if (vFileInfos->fileStyle.use_count()) {
1531
0
                    return true;
1532
0
                }
1533
0
            }
1534
0
        }
1535
0
    }
1536
1537
0
    return false;
1538
0
}
1539
1540
0
void IGFD::FilterManager::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) {
1541
0
    std::string _criteria;
1542
0
    if (vCriteria) _criteria = std::string(vCriteria);
1543
0
    m_FilesStyle[vFlags][_criteria]        = std::make_shared<FileStyle>(vColor, vIcon, vFont);
1544
0
    m_FilesStyle[vFlags][_criteria]->flags = vFlags;
1545
0
}
1546
1547
0
void IGFD::FilterManager::SetFileStyle(FileStyle::FileStyleFunctor vFunctor) {
1548
0
    if (vFunctor) {
1549
0
        m_FilesStyleFunctors.push_back(vFunctor);
1550
0
    }
1551
0
}
1552
1553
// todo : refactor this fucking function
1554
0
bool IGFD::FilterManager::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont** vOutFont) {
1555
0
    if (vOutColor) {
1556
0
        if (!m_FilesStyle.empty()) {
1557
0
            if (m_FilesStyle.find(vFlags) != m_FilesStyle.end()) {  // found
1558
0
                if (vFlags & IGFD_FileStyleByContainedInFullName) {
1559
                    // search for vCriteria who are containing the criteria
1560
0
                    for (const auto& _file : m_FilesStyle.at(vFlags)) {
1561
0
                        if (vCriteria.find(_file.first) != std::string::npos) {
1562
0
                            if (_file.second.use_count()) {
1563
0
                                *vOutColor = _file.second->color;
1564
0
                                if (vOutIcon) *vOutIcon = _file.second->icon;
1565
0
                                if (vOutFont) *vOutFont = _file.second->font;
1566
0
                                return true;
1567
0
                            }
1568
0
                        }
1569
0
                    }
1570
0
                } else {
1571
0
                    if (m_FilesStyle.at(vFlags).find(vCriteria) != m_FilesStyle.at(vFlags).end()) {  // found
1572
0
                        *vOutColor = m_FilesStyle[vFlags][vCriteria]->color;
1573
0
                        if (vOutIcon) *vOutIcon = m_FilesStyle[vFlags][vCriteria]->icon;
1574
0
                        if (vOutFont) *vOutFont = m_FilesStyle[vFlags][vCriteria]->font;
1575
0
                        return true;
1576
0
                    }
1577
0
                }
1578
0
            } else {
1579
                // search for flag composition
1580
0
                for (const auto& _flag : m_FilesStyle) {
1581
0
                    if (_flag.first & vFlags) {
1582
0
                        if (_flag.first & IGFD_FileStyleByContainedInFullName) {
1583
                            // search for vCriteria who are containing the criteria
1584
0
                            for (const auto& _file : m_FilesStyle.at(_flag.first)) {
1585
0
                                if (vCriteria.find(_file.first) != std::string::npos) {
1586
0
                                    if (_file.second.use_count()) {
1587
0
                                        *vOutColor = _file.second->color;
1588
0
                                        if (vOutIcon) *vOutIcon = _file.second->icon;
1589
0
                                        if (vOutFont) *vOutFont = _file.second->font;
1590
0
                                        return true;
1591
0
                                    }
1592
0
                                }
1593
0
                            }
1594
0
                        } else {
1595
0
                            if (m_FilesStyle.at(_flag.first).find(vCriteria) != m_FilesStyle.at(_flag.first).end()) {  // found
1596
0
                                *vOutColor = m_FilesStyle[_flag.first][vCriteria]->color;
1597
0
                                if (vOutIcon) *vOutIcon = m_FilesStyle[_flag.first][vCriteria]->icon;
1598
0
                                if (vOutFont) *vOutFont = m_FilesStyle[_flag.first][vCriteria]->font;
1599
0
                                return true;
1600
0
                            }
1601
0
                        }
1602
0
                    }
1603
0
                }
1604
0
            }
1605
0
        }
1606
0
    }
1607
0
    return false;
1608
0
}
1609
1610
0
void IGFD::FilterManager::ClearFilesStyle() {
1611
0
    m_FilesStyle.clear();
1612
0
}
1613
1614
0
bool IGFD::FilterManager::IsCoveredByFilters(const FileInfos& vFileInfos, bool vIsCaseInsensitive) const {
1615
0
    if (!dLGFilters.empty() && !m_SelectedFilter.empty()) {
1616
0
        return (m_SelectedFilter.exist(vFileInfos, vIsCaseInsensitive) || m_SelectedFilter.regexExist(vFileInfos.fileNameExt));
1617
0
    }
1618
1619
0
    return false;
1620
0
}
1621
1622
0
float IGFD::FilterManager::GetFilterComboBoxWidth() const {
1623
0
#if FILTER_COMBO_AUTO_SIZE
1624
0
    const auto& combo_width = ImGui::CalcTextSize(m_SelectedFilter.title.c_str()).x + ImGui::GetFrameHeight() + ImGui::GetStyle().ItemInnerSpacing.x;
1625
0
    return ImMax(combo_width, FILTER_COMBO_MIN_WIDTH);
1626
#else
1627
    return FILTER_COMBO_MIN_WIDTH;
1628
#endif
1629
0
}
1630
1631
0
bool IGFD::FilterManager::DrawFilterComboBox(FileDialogInternal& vFileDialogInternal) {
1632
0
    if (!dLGFilters.empty()) {
1633
0
        ImGui::SameLine();
1634
0
        bool needToApllyNewFilter = false;
1635
0
        ImGui::PushItemWidth(GetFilterComboBoxWidth());
1636
0
        if (IMGUI_BEGIN_COMBO("##Filters", m_SelectedFilter.title.c_str(), ImGuiComboFlags_None)) {
1637
0
            intptr_t i = 0;
1638
0
            for (const auto& filter : m_ParsedFilters) {
1639
0
                const bool item_selected = (filter.title == m_SelectedFilter.title);
1640
0
                ImGui::PushID((void*)(intptr_t)i++);
1641
0
                if (ImGui::Selectable(filter.title.c_str(), item_selected)) {
1642
0
                    m_SelectedFilter     = filter;
1643
0
                    needToApllyNewFilter = true;
1644
0
                }
1645
0
                ImGui::PopID();
1646
0
            }
1647
0
            ImGui::EndCombo();
1648
0
        }
1649
0
        ImGui::PopItemWidth();
1650
0
        if (needToApllyNewFilter) {
1651
0
            vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
1652
0
        }
1653
0
        return needToApllyNewFilter;
1654
0
    }
1655
0
    return false;
1656
0
}
1657
1658
0
std::string IGFD::FilterManager::ReplaceExtentionWithCurrentFilterIfNeeded(const std::string& vFileName, IGFD_ResultMode vFlag) const {
1659
0
    auto result = vFileName;
1660
0
    if (!result.empty()) {
1661
0
        const auto& current_filter = m_SelectedFilter.getFirstFilter();
1662
0
        if (!current_filter.empty()) {
1663
0
            Utils::ReplaceString(result, "..", ".");
1664
1665
            // is a regex => no change
1666
0
            if (current_filter.find("((") != std::string::npos) {
1667
0
                return result;
1668
0
            }
1669
1670
            // contain .* => no change
1671
0
            if (current_filter.find(".*") != std::string::npos) {
1672
0
                return result;
1673
0
            }
1674
1675
0
            switch (vFlag) {
1676
0
                case IGFD_ResultMode_KeepInputFile: {
1677
0
                    return vFileName;
1678
0
                }
1679
0
                case IGFD_ResultMode_OverwriteFileExt: {
1680
0
                    const auto& count_dots = Utils::GetCharCountInString(vFileName, '.');
1681
0
                    const auto& min_dots   = ImMin<size_t>(count_dots, m_SelectedFilter.count_dots);
1682
0
                    const auto& lp         = Utils::GetLastCharPosWithMinCharCount(vFileName, '.', min_dots);
1683
0
                    if (lp != std::string::npos) {  // there is a user extention
1684
0
                        const auto& file_name_without_user_ext = vFileName.substr(0, lp);
1685
0
                        result                                 = file_name_without_user_ext + current_filter;
1686
0
                    } else {  // add extention
1687
0
                        result = vFileName + current_filter;
1688
0
                    }
1689
0
                    break;
1690
0
                }
1691
0
                case IGFD_ResultMode_AddIfNoFileExt: {
1692
0
                    const auto& count_dots = Utils::GetCharCountInString(vFileName, '.');
1693
0
                    const auto& min_dots   = ImMin<size_t>(count_dots, m_SelectedFilter.count_dots);
1694
0
                    const auto& lp         = Utils::GetLastCharPosWithMinCharCount(vFileName, '.', min_dots);
1695
0
                    if (lp == std::string::npos ||        // there is no user extention
1696
0
                        lp == (vFileName.size() - 1U)) {  // or this pos is also the last char => considered like no user extention
1697
0
                        const auto& file_name_without_user_ext = vFileName.substr(0, lp);
1698
0
                        result                                 = file_name_without_user_ext + current_filter;
1699
0
                    }
1700
0
                    break;
1701
0
                }
1702
0
                default: break;
1703
0
            }
1704
1705
0
            Utils::ReplaceString(result, "..", ".");
1706
0
        }
1707
0
    }
1708
0
    return result;
1709
0
}
1710
1711
0
void IGFD::FilterManager::SetDefaultFilterIfNotDefined() {
1712
0
    if (m_SelectedFilter.empty() &&                   // no filter selected
1713
0
        !m_ParsedFilters.empty()) {                   // filter exist
1714
0
        m_SelectedFilter = *m_ParsedFilters.begin();  // we take the first filter
1715
0
    }
1716
0
}
1717
1718
0
IGFD::FileType::FileType() = default;
1719
0
IGFD::FileType::FileType(const ContentType& vContentType, const bool vIsSymlink) : m_Content(vContentType), m_Symlink(vIsSymlink) {
1720
0
}
1721
0
void IGFD::FileType::SetContent(const ContentType& vContentType) {
1722
0
    m_Content = vContentType;
1723
0
}
1724
0
void IGFD::FileType::SetSymLink(const bool vIsSymlink) {
1725
0
    m_Symlink = vIsSymlink;
1726
0
}
1727
0
bool IGFD::FileType::isValid() const {
1728
0
    return m_Content != ContentType::Invalid;
1729
0
}
1730
0
bool IGFD::FileType::isDir() const {
1731
0
    return m_Content == ContentType::Directory;
1732
0
}
1733
0
bool IGFD::FileType::isFile() const {
1734
0
    return m_Content == ContentType::File;
1735
0
}
1736
0
bool IGFD::FileType::isLinkToUnknown() const {
1737
0
    return m_Content == ContentType::LinkToUnknown;
1738
0
}
1739
0
bool IGFD::FileType::isSymLink() const {
1740
0
    return m_Symlink;
1741
0
}
1742
// Comparisons only care about the content type, ignoring whether it's a symlink or not.
1743
0
bool IGFD::FileType::operator==(const FileType& rhs) const {
1744
0
    return m_Content == rhs.m_Content;
1745
0
}
1746
0
bool IGFD::FileType::operator!=(const FileType& rhs) const {
1747
0
    return m_Content != rhs.m_Content;
1748
0
}
1749
0
bool IGFD::FileType::operator<(const FileType& rhs) const {
1750
0
    return m_Content < rhs.m_Content;
1751
0
}
1752
0
bool IGFD::FileType::operator>(const FileType& rhs) const {
1753
0
    return m_Content > rhs.m_Content;
1754
0
}
1755
1756
0
std::shared_ptr<IGFD::FileInfos> IGFD::FileInfos::create() {
1757
0
    return std::make_shared<IGFD::FileInfos>();
1758
0
}
1759
1760
0
bool IGFD::FileInfos::SearchForTag(const std::string& vTag) const {
1761
0
    if (!vTag.empty()) {
1762
0
        if (fileNameExt_optimized == "..") return true;
1763
0
        return fileNameExt_optimized.find(vTag) != std::string::npos ||  // first try without case and accents
1764
0
               fileNameExt.find(vTag) != std::string::npos;              // second if searched with case and accents
1765
0
    }
1766
1767
    // if tag is empty => its a special case but all is found
1768
0
    return true;
1769
0
}
1770
1771
0
bool IGFD::FileInfos::SearchForExt(const std::string& vExt, const bool vIsCaseInsensitive, const size_t& vMaxLevel) const {
1772
0
    if (!vExt.empty()) {
1773
0
        const auto& ext_to_check = vIsCaseInsensitive ? Utils::LowerCaseString(vExt) : vExt;
1774
0
        const auto& ext_levels   = vIsCaseInsensitive ? fileExtLevels_optimized : fileExtLevels;
1775
0
        if (vMaxLevel >= 1 && countExtDot >= vMaxLevel) {
1776
0
            for (const auto& ext : ext_levels) {
1777
0
                if (!ext.empty() && ext == ext_to_check) {
1778
0
                    return true;
1779
0
                }
1780
0
            }
1781
0
        } else {
1782
0
            return (fileExtLevels[0] == vExt);
1783
0
        }
1784
0
    }
1785
0
    return false;
1786
0
}
1787
1788
0
bool IGFD::FileInfos::SearchForExts(const std::string& vComaSepExts, const bool vIsCaseInsensitive, const size_t& vMaxLevel) const {
1789
0
    if (!vComaSepExts.empty()) {
1790
0
        const auto& arr = Utils::SplitStringToVector(vComaSepExts, ',', false);
1791
0
        for (const auto& a : arr) {
1792
0
            if (SearchForExt(a, vIsCaseInsensitive, vMaxLevel)) {
1793
0
                return true;
1794
0
            }
1795
0
        }
1796
0
    }
1797
0
    return false;
1798
0
}
1799
1800
0
bool IGFD::FileInfos::FinalizeFileTypeParsing(const size_t& vMaxDotToExtract) {
1801
0
    if (fileType.isFile() || fileType.isLinkToUnknown()) {  // link can have the same extention of a file
1802
0
        countExtDot = Utils::GetCharCountInString(fileNameExt, '.');
1803
0
        size_t lpt  = 0U;
1804
0
        if (countExtDot > 1U) {  // multi layer ext
1805
0
            size_t max_dot_to_extract = vMaxDotToExtract;
1806
0
            if (max_dot_to_extract > countExtDot) {
1807
0
                max_dot_to_extract = countExtDot;
1808
0
            }
1809
0
            lpt = Utils::GetLastCharPosWithMinCharCount(fileNameExt, '.', max_dot_to_extract);
1810
0
        } else {
1811
0
            lpt = fileNameExt.find_first_of('.');
1812
0
        }
1813
0
        if (lpt != std::string::npos) {
1814
0
            size_t lvl                   = 0U;
1815
0
            fileNameLevels[lvl]          = fileNameExt.substr(0, lpt);
1816
0
            fileNameLevels[lvl]          = Utils::LowerCaseString(fileNameLevels[lvl]);
1817
0
            fileExtLevels[lvl]           = fileNameExt.substr(lpt);
1818
0
            fileExtLevels_optimized[lvl] = Utils::LowerCaseString(fileExtLevels[lvl]);
1819
0
            if (countExtDot > 1U) {  // multi layer ext
1820
0
                auto count = countExtDot;
1821
0
                while (count > 0 && lpt != std::string::npos && lvl < fileExtLevels.size()) {
1822
0
                    ++lpt;
1823
0
                    ++lvl;
1824
0
                    if (fileNameExt.size() > lpt) {
1825
0
                        lpt = fileNameExt.find_first_of('.', lpt);
1826
0
                        if (lpt != std::string::npos) {
1827
0
                            fileNameLevels[lvl]          = fileNameExt.substr(0, lpt);
1828
0
                            fileNameLevels[lvl]          = Utils::LowerCaseString(fileNameLevels[lvl]);
1829
0
                            fileExtLevels[lvl]           = fileNameExt.substr(lpt);
1830
0
                            fileExtLevels_optimized[lvl] = Utils::LowerCaseString(fileExtLevels[lvl]);
1831
0
                        }
1832
0
                    }
1833
0
                }
1834
0
            }
1835
0
        }
1836
0
        return true;
1837
0
    }
1838
0
    return false;
1839
0
}
1840
1841
1
IGFD::FileManager::FileManager() {
1842
1
    fsRoot = IGFD::Utils::GetPathSeparator();
1843
1
#define STR(x) #x
1844
1
#define STR_AFTER_EXPAND(x) STR(x)
1845
1
    m_FileSystemName = STR_AFTER_EXPAND(FILE_SYSTEM_OVERRIDE);
1846
1
#undef STR_AFTER_EXPAND
1847
1
#undef STR
1848
    // std::make_unique is not available un cpp11
1849
1
    m_FileSystemPtr = std::unique_ptr<FILE_SYSTEM_OVERRIDE>(new FILE_SYSTEM_OVERRIDE());
1850
    // m_FileSystemPtr = std::make_unique<FILE_SYSTEM_OVERRIDE>();
1851
1
}
1852
1853
0
void IGFD::FileManager::OpenCurrentPath(const FileDialogInternal& vFileDialogInternal) {
1854
0
    showDevices = false;
1855
0
    ClearComposer();
1856
0
    ClearFileLists();
1857
0
    if (dLGDirectoryMode) {  // directory mode
1858
0
        SetDefaultFileName(".");
1859
0
    } else {
1860
0
        SetDefaultFileName(dLGDefaultFileName);
1861
0
    }
1862
0
    ScanDir(vFileDialogInternal, GetCurrentPath());
1863
0
}
1864
1865
0
void IGFD::FileManager::SortFields(const FileDialogInternal& vFileDialogInternal) {
1866
0
    m_SortFields(vFileDialogInternal, m_FileList, m_FilteredFileList);
1867
0
}
1868
1869
0
bool IGFD::FileManager::M_SortStrings(const FileDialogInternal& vFileDialogInternal, const bool vInsensitiveCase, const bool vDescendingOrder, const std::string& vA, const std::string& vB) {
1870
0
    if (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_NaturalSorting) {
1871
0
        return IGFD::Utils::NaturalCompare(vA, vB, vInsensitiveCase, vDescendingOrder);
1872
0
    } else if (vInsensitiveCase) {
1873
0
        const auto ret = stricmp(vA.c_str(), vB.c_str());
1874
0
        return vDescendingOrder ? (ret > 0) : (ret < 0);
1875
0
    } else {
1876
0
        const auto ret = strcmp(vA.c_str(), vB.c_str());
1877
0
        return vDescendingOrder ? (ret > 0) : (ret < 0);
1878
0
    }
1879
0
}
1880
1881
0
void IGFD::FileManager::m_SortFields(const FileDialogInternal& vFileDialogInternal, std::vector<std::shared_ptr<FileInfos> >& vFileInfosList, std::vector<std::shared_ptr<FileInfos> >& vFileInfosFilteredList) {
1882
0
    if (sortingField != SortingFieldEnum::FIELD_NONE) {
1883
0
        headerFileName = tableHeaderFileNameString;
1884
0
        headerFileType = tableHeaderFileTypeString;
1885
0
        headerFileSize = tableHeaderFileSizeString;
1886
0
        headerFileDate = tableHeaderFileDateString;
1887
#ifdef USE_THUMBNAILS
1888
        headerFileThumbnails = tableHeaderFileThumbnailsString;
1889
#endif  // #ifdef USE_THUMBNAILS
1890
0
    }
1891
0
    if (sortingField == SortingFieldEnum::FIELD_FILENAME) {
1892
0
        if (sortingDirection[0]) {
1893
#ifdef USE_CUSTOM_SORTING_ICON
1894
            headerFileName = tableHeaderAscendingIcon + headerFileName;
1895
#endif                                                               // USE_CUSTOM_SORTING_ICON
1896
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(),  //
1897
0
                      [&vFileDialogInternal](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1898
0
                          if (!a.use_count() || !b.use_count()) return false;
1899
0
                          if (a->fileType != b->fileType) return (a->fileType < b->fileType);                      // directories first
1900
0
                          return M_SortStrings(vFileDialogInternal, true, false, a->fileNameExt, b->fileNameExt);  // sort in insensitive case
1901
0
                      });
1902
0
        } else {
1903
#ifdef USE_CUSTOM_SORTING_ICON
1904
            headerFileName = tableHeaderDescendingIcon + headerFileName;
1905
#endif                                                               // USE_CUSTOM_SORTING_ICON
1906
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(),  //
1907
0
                      [&vFileDialogInternal](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1908
0
                          if (!a.use_count() || !b.use_count()) return false;
1909
0
                          if (a->fileType != b->fileType) return (a->fileType > b->fileType);                     // directories last
1910
0
                          return M_SortStrings(vFileDialogInternal, true, true, a->fileNameExt, b->fileNameExt);  // sort in insensitive case
1911
0
                      });
1912
0
        }
1913
0
    } else if (sortingField == SortingFieldEnum::FIELD_TYPE) {
1914
0
        if (sortingDirection[1]) {
1915
#ifdef USE_CUSTOM_SORTING_ICON
1916
            headerFileType = tableHeaderAscendingIcon + headerFileType;
1917
#endif  // USE_CUSTOM_SORTING_ICON
1918
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [&vFileDialogInternal](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1919
0
                if (!a.use_count() || !b.use_count()) return false;
1920
0
                if (a->fileType != b->fileType) return (a->fileType < b->fileType);                                // directory in first
1921
0
                return M_SortStrings(vFileDialogInternal, true, false, a->fileExtLevels[0], b->fileExtLevels[0]);  // sort in sensitive case
1922
0
            });
1923
0
        } else {
1924
#ifdef USE_CUSTOM_SORTING_ICON
1925
            headerFileType = tableHeaderDescendingIcon + headerFileType;
1926
#endif  // USE_CUSTOM_SORTING_ICON
1927
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [&vFileDialogInternal](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1928
0
                if (!a.use_count() || !b.use_count()) return false;
1929
0
                if (a->fileType != b->fileType) return (a->fileType > b->fileType);                               // directory in last
1930
0
                return M_SortStrings(vFileDialogInternal, true, true, a->fileExtLevels[0], b->fileExtLevels[0]);  // sort in sensitive case
1931
0
            });
1932
0
        }
1933
0
    } else if (sortingField == SortingFieldEnum::FIELD_SIZE) {
1934
0
        if (sortingDirection[2]) {
1935
#ifdef USE_CUSTOM_SORTING_ICON
1936
            headerFileSize = tableHeaderAscendingIcon + headerFileSize;
1937
#endif  // USE_CUSTOM_SORTING_ICON
1938
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1939
0
                if (!a.use_count() || !b.use_count()) return false;
1940
0
                if (a->fileType != b->fileType) return (a->fileType < b->fileType);  // directory in first
1941
0
                return (a->fileSize < b->fileSize);                                  // else
1942
0
            });
1943
0
        } else {
1944
#ifdef USE_CUSTOM_SORTING_ICON
1945
            headerFileSize = tableHeaderDescendingIcon + headerFileSize;
1946
#endif  // USE_CUSTOM_SORTING_ICON
1947
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1948
0
                if (!a.use_count() || !b.use_count()) return false;
1949
0
                if (a->fileType != b->fileType) return (a->fileType > b->fileType);  // directory in last
1950
0
                return (a->fileSize > b->fileSize);                                  // else
1951
0
            });
1952
0
        }
1953
0
    } else if (sortingField == SortingFieldEnum::FIELD_DATE) {
1954
0
        if (sortingDirection[3]) {
1955
#ifdef USE_CUSTOM_SORTING_ICON
1956
            headerFileDate = tableHeaderAscendingIcon + headerFileDate;
1957
#endif  // USE_CUSTOM_SORTING_ICON
1958
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1959
0
                if (!a.use_count() || !b.use_count()) return false;
1960
0
                if (a->fileType != b->fileType) return (a->fileType < b->fileType);  // directory in first
1961
0
                return (a->fileModifDate < b->fileModifDate);                        // else
1962
0
            });
1963
0
        } else {
1964
#ifdef USE_CUSTOM_SORTING_ICON
1965
            headerFileDate = tableHeaderDescendingIcon + headerFileDate;
1966
#endif  // USE_CUSTOM_SORTING_ICON
1967
0
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1968
0
                if (!a.use_count() || !b.use_count()) return false;
1969
0
                if (a->fileType != b->fileType) return (a->fileType > b->fileType);  // directory in last
1970
0
                return (a->fileModifDate > b->fileModifDate);                        // else
1971
0
            });
1972
0
        }
1973
0
    }
1974
#ifdef USE_THUMBNAILS
1975
    else if (sortingField == SortingFieldEnum::FIELD_THUMBNAILS) {
1976
        // we will compare thumbnails by :
1977
        // 1) width
1978
        // 2) height
1979
1980
        if (sortingDirection[4]) {
1981
#ifdef USE_CUSTOM_SORTING_ICON
1982
            headerFileThumbnails = tableHeaderAscendingIcon + headerFileThumbnails;
1983
#endif  // USE_CUSTOM_SORTING_ICON
1984
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1985
                if (!a.use_count() || !b.use_count()) return false;
1986
                if (a->fileType != b->fileType) return (a->fileType.isDir());  // directory in first
1987
                if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) return (a->thumbnailInfo.textureHeight < b->thumbnailInfo.textureHeight);
1988
                return (a->thumbnailInfo.textureWidth < b->thumbnailInfo.textureWidth);
1989
            });
1990
        }
1991
1992
        else {
1993
#ifdef USE_CUSTOM_SORTING_ICON
1994
            headerFileThumbnails = tableHeaderDescendingIcon + headerFileThumbnails;
1995
#endif  // USE_CUSTOM_SORTING_ICON
1996
            std::sort(vFileInfosList.begin(), vFileInfosList.end(), [](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool {
1997
                if (!a.use_count() || !b.use_count()) return false;
1998
                if (a->fileType != b->fileType) return (!a->fileType.isDir());  // directory in last
1999
                if (a->thumbnailInfo.textureWidth == b->thumbnailInfo.textureWidth) return (a->thumbnailInfo.textureHeight > b->thumbnailInfo.textureHeight);
2000
                return (a->thumbnailInfo.textureWidth > b->thumbnailInfo.textureWidth);
2001
            });
2002
        }
2003
    }
2004
#endif  // USE_THUMBNAILS
2005
2006
0
    m_ApplyFilteringOnFileList(vFileDialogInternal, vFileInfosList, vFileInfosFilteredList);
2007
0
}
2008
2009
0
bool IGFD::FileManager::m_CompleteFileInfosWithUserFileAttirbutes(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos) {
2010
0
    if (vFileDialogInternal.getDialogConfig().userFileAttributes != nullptr) {
2011
0
        if (!vFileDialogInternal.getDialogConfig().userFileAttributes(vInfos.get(), vFileDialogInternal.getDialogConfig().userDatas)) {
2012
0
            return false;  // the file will be ignored, so not added to the file list, so not displayed
2013
0
        } else {
2014
0
            if (!vInfos->fileType.isDir()) {
2015
0
                vInfos->formatedFileSize = IGFD::Utils::FormatFileSize(vInfos->fileSize);
2016
0
            }
2017
0
        }
2018
0
    }
2019
0
    return true;  // file will be added to file list, so displayed
2020
0
}
2021
2022
0
void IGFD::FileManager::ClearFileLists() {
2023
0
    m_FilteredFileList.clear();
2024
0
    m_FileList.clear();
2025
0
    m_SelectedFileNames.clear();
2026
0
}
2027
2028
0
void IGFD::FileManager::ClearPathLists() {
2029
0
    m_FilteredPathList.clear();
2030
0
    m_PathList.clear();
2031
0
    m_SelectedFileNames.clear();
2032
0
}
2033
2034
0
void IGFD::FileManager::m_AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const FileType& vFileType) {
2035
0
    auto pInfos = FileInfos::create();
2036
2037
0
    pInfos->filePath              = vPath;
2038
0
    pInfos->fileNameExt           = vFileName;
2039
0
    pInfos->fileNameExt_optimized = Utils::LowerCaseString(pInfos->fileNameExt);
2040
0
    pInfos->fileType              = vFileType;
2041
2042
0
    if (pInfos->fileNameExt.empty() || (pInfos->fileNameExt == "." && !vFileDialogInternal.filterManager.dLGFilters.empty())) {  // filename empty or filename is the current dir '.' //-V807
2043
0
        return;
2044
0
    }
2045
2046
0
    if (pInfos->fileNameExt != ".." && (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DontShowHiddenFiles) && pInfos->fileNameExt[0] == '.') {  // dont show hidden files
2047
0
        if (!vFileDialogInternal.filterManager.dLGFilters.empty() || (vFileDialogInternal.filterManager.dLGFilters.empty() && pInfos->fileNameExt != ".")) {            // except "." if in directory mode //-V728
2048
0
            return;
2049
0
        }
2050
0
    }
2051
2052
0
    if (pInfos->FinalizeFileTypeParsing(vFileDialogInternal.filterManager.GetSelectedFilter().count_dots)) {
2053
0
        if (!vFileDialogInternal.filterManager.IsCoveredByFilters(*pInfos.get(),  //
2054
0
                                                                  (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_CaseInsensitiveExtentionFiltering) != 0)) {
2055
0
            return;
2056
0
        }
2057
0
    }
2058
2059
0
    vFileDialogInternal.filterManager.FillFileStyle(pInfos);
2060
2061
0
    m_CompleteFileInfos(pInfos);
2062
2063
0
    if (m_CompleteFileInfosWithUserFileAttirbutes(vFileDialogInternal, pInfos)) {
2064
0
        m_FileList.push_back(pInfos);
2065
0
    }
2066
0
}
2067
2068
0
void IGFD::FileManager::m_AddPath(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName, const FileType& vFileType) {
2069
0
    if (!vFileType.isDir()) return;
2070
2071
0
    auto pInfos = FileInfos::create();
2072
2073
0
    pInfos->filePath              = vPath;
2074
0
    pInfos->fileNameExt           = vFileName;
2075
0
    pInfos->fileNameExt_optimized = Utils::LowerCaseString(pInfos->fileNameExt);
2076
0
    pInfos->fileType              = vFileType;
2077
2078
0
    if (pInfos->fileNameExt.empty() || (pInfos->fileNameExt == "." && !vFileDialogInternal.filterManager.dLGFilters.empty())) {  // filename empty or filename is the current dir '.' //-V807
2079
0
        return;
2080
0
    }
2081
2082
0
    if (pInfos->fileNameExt != ".." && (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DontShowHiddenFiles) && pInfos->fileNameExt[0] == '.') {  // dont show hidden files
2083
0
        if (!vFileDialogInternal.filterManager.dLGFilters.empty() || (vFileDialogInternal.filterManager.dLGFilters.empty() && pInfos->fileNameExt != ".")) {            // except "." if in directory mode //-V728
2084
0
            return;
2085
0
        }
2086
0
    }
2087
2088
0
    vFileDialogInternal.filterManager.FillFileStyle(pInfos);
2089
2090
0
    m_CompleteFileInfos(pInfos);
2091
2092
0
    if (m_CompleteFileInfosWithUserFileAttirbutes(vFileDialogInternal, pInfos)) {
2093
0
        m_PathList.push_back(pInfos);
2094
0
    }
2095
0
}
2096
2097
0
void IGFD::FileManager::ScanDir(const FileDialogInternal& vFileDialogInternal, const std::string& vPath) {
2098
0
    std::string path = vPath;
2099
2100
0
    if (m_CurrentPathDecomposition.empty()) {
2101
0
        SetCurrentDir(path);
2102
0
    }
2103
2104
0
    if (!m_CurrentPathDecomposition.empty()) {
2105
#ifdef _IGFD_WIN_
2106
        if (path == fsRoot) {
2107
            path += IGFD::Utils::GetPathSeparator();
2108
        }
2109
#endif  // _IGFD_WIN_
2110
2111
0
        ClearFileLists();
2112
2113
0
        const auto& files = m_FileSystemPtr->ScanDirectory(path);
2114
0
        for (const auto& file : files) {
2115
0
            m_AddFile(vFileDialogInternal, path, file.fileNameExt, file.fileType);
2116
0
        }
2117
2118
0
        m_SortFields(vFileDialogInternal, m_FileList, m_FilteredFileList);
2119
0
    }
2120
0
}
2121
2122
0
void IGFD::FileManager::m_ScanDirForPathSelection(const FileDialogInternal& vFileDialogInternal, const std::string& vPath) {
2123
0
    std::string path = vPath;
2124
2125
0
    if (!path.empty()) {
2126
#ifdef _IGFD_WIN_
2127
        if (path == fsRoot) path += IGFD::Utils::GetPathSeparator();
2128
#endif  // _IGFD_WIN_
2129
2130
0
        ClearPathLists();
2131
2132
0
        const auto& files = m_FileSystemPtr->ScanDirectory(path);
2133
0
        for (const auto& file : files) {
2134
0
            if (file.fileType.isDir()) {
2135
0
                m_AddPath(vFileDialogInternal, path, file.fileNameExt, file.fileType);
2136
0
            }
2137
0
        }
2138
2139
0
        m_SortFields(vFileDialogInternal, m_PathList, m_FilteredPathList);
2140
0
    }
2141
0
}
2142
2143
0
void IGFD::FileManager::m_OpenPathPopup(const FileDialogInternal& vFileDialogInternal, std::vector<std::string>::iterator vPathIter) {
2144
0
    const auto path = ComposeNewPath(vPathIter);
2145
0
    m_ScanDirForPathSelection(vFileDialogInternal, path);
2146
0
    m_PopupComposedPath = vPathIter;
2147
0
    ImGui::OpenPopup("IGFD_Path_Popup");
2148
0
}
2149
2150
0
bool IGFD::FileManager::GetDevices() {
2151
0
    const auto devices = m_FileSystemPtr->GetDevicesList();
2152
0
    if (!devices.empty()) {
2153
0
        m_CurrentPath.clear();
2154
0
        m_CurrentPathDecomposition.clear();
2155
0
        ClearFileLists();
2156
0
        for (const auto& drive : devices) {
2157
0
            auto pInfo                   = FileInfos::create();
2158
0
            pInfo->fileNameExt           = drive.first;
2159
0
            pInfo->fileNameExt_optimized = Utils::LowerCaseString(drive.first);
2160
0
            pInfo->deviceInfos           = drive.second;
2161
0
            pInfo->fileType.SetContent(FileType::ContentType::Directory);
2162
0
            if (!pInfo->fileNameExt.empty()) {
2163
0
                m_FileList.push_back(pInfo);
2164
0
                showDevices = true;
2165
0
            }
2166
0
        }
2167
0
        return true;
2168
0
    }
2169
0
    return false;
2170
0
}
2171
2172
0
bool IGFD::FileManager::IsComposerEmpty() const {
2173
0
    return m_CurrentPathDecomposition.empty();
2174
0
}
2175
2176
0
size_t IGFD::FileManager::GetComposerSize() const {
2177
0
    return m_CurrentPathDecomposition.size();
2178
0
}
2179
2180
0
bool IGFD::FileManager::IsFileListEmpty() const {
2181
0
    return m_FileList.empty();
2182
0
}
2183
2184
0
bool IGFD::FileManager::IsPathListEmpty() const {
2185
0
    return m_PathList.empty();
2186
0
}
2187
2188
0
size_t IGFD::FileManager::GetFullFileListSize() const {
2189
0
    return m_FileList.size();
2190
0
}
2191
2192
0
std::shared_ptr<IGFD::FileInfos> IGFD::FileManager::GetFullFileAt(size_t vIdx) {
2193
0
    if (vIdx < m_FileList.size()) return m_FileList[vIdx];
2194
0
    return nullptr;
2195
0
}
2196
2197
0
bool IGFD::FileManager::IsFilteredListEmpty() const {
2198
0
    return m_FilteredFileList.empty();
2199
0
}
2200
2201
0
bool IGFD::FileManager::IsPathFilteredListEmpty() const {
2202
0
    return m_FilteredPathList.empty();
2203
0
}
2204
2205
0
size_t IGFD::FileManager::GetFilteredListSize() const {
2206
0
    return m_FilteredFileList.size();
2207
0
}
2208
2209
0
size_t IGFD::FileManager::GetPathFilteredListSize() const {
2210
0
    return m_FilteredPathList.size();
2211
0
}
2212
2213
0
std::shared_ptr<IGFD::FileInfos> IGFD::FileManager::GetFilteredFileAt(size_t vIdx) {
2214
0
    if (vIdx < m_FilteredFileList.size()) return m_FilteredFileList[vIdx];
2215
0
    return nullptr;
2216
0
}
2217
2218
0
std::shared_ptr<IGFD::FileInfos> IGFD::FileManager::GetFilteredPathAt(size_t vIdx) {
2219
0
    if (vIdx < m_FilteredPathList.size()) return m_FilteredPathList[vIdx];
2220
0
    return nullptr;
2221
0
}
2222
2223
0
std::vector<std::string>::iterator IGFD::FileManager::GetCurrentPopupComposedPath() const {
2224
0
    return m_PopupComposedPath;
2225
0
}
2226
2227
0
bool IGFD::FileManager::IsFileNameSelected(const std::string& vFileName) {
2228
0
    return m_SelectedFileNames.find(vFileName) != m_SelectedFileNames.end();
2229
0
}
2230
2231
0
std::string IGFD::FileManager::GetBack() {
2232
0
    return m_CurrentPathDecomposition.back();
2233
0
}
2234
2235
0
void IGFD::FileManager::ClearComposer() {
2236
0
    m_CurrentPathDecomposition.clear();
2237
0
}
2238
2239
0
void IGFD::FileManager::ClearAll() {
2240
0
    ClearComposer();
2241
0
    ClearFileLists();
2242
0
    ClearPathLists();
2243
0
}
2244
0
void IGFD::FileManager::ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal) {
2245
0
    m_ApplyFilteringOnFileList(vFileDialogInternal, m_FileList, m_FilteredFileList);
2246
0
}
2247
2248
0
void IGFD::FileManager::m_ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal, std::vector<std::shared_ptr<FileInfos> >& vFileInfosList, std::vector<std::shared_ptr<FileInfos> >& vFileInfosFilteredList) {
2249
0
    vFileInfosFilteredList.clear();
2250
0
    for (const auto& file : vFileInfosList) {
2251
0
        if (!file.use_count()) continue;
2252
0
        bool show = true;
2253
0
        if (!file->SearchForTag(vFileDialogInternal.searchManager.searchTag))  // if search tag
2254
0
            show = false;
2255
0
        if (dLGDirectoryMode && !file->fileType.isDir()) show = false;
2256
0
        if (show) vFileInfosFilteredList.push_back(file);
2257
0
    }
2258
0
}
2259
2260
0
void IGFD::FileManager::m_CompleteFileInfos(const std::shared_ptr<FileInfos>& vInfos) {
2261
0
    if (!vInfos.use_count()) return;
2262
2263
0
    if ((vInfos->fileNameExt == ".") ||   // current dir (special case, not really a dir or a file)
2264
0
        (vInfos->fileNameExt == "..")) {  // last dir (special case, not really a dir or a file)
2265
0
        return;
2266
0
    }
2267
2268
    // _stat struct :
2269
    // dev_t     st_dev;     /* ID of device containing file */
2270
    // ino_t     st_ino;     /* inode number */
2271
    // mode_t    st_mode;    /* protection */
2272
    // nlink_t   st_nlink;   /* number of hard links */
2273
    // uid_t     st_uid;     /* user ID of owner */
2274
    // gid_t     st_gid;     /* group ID of owner */
2275
    // dev_t     st_rdev;    /* device ID (if special file) */
2276
    // off_t     st_size;    /* total size, in bytes */
2277
    // blksize_t st_blksize; /* blocksize for file system I/O */
2278
    // blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
2279
    // time_t    st_atime;   /* time of last access - not sure out of ntfs */
2280
    // time_t    st_mtime;   /* time of last modification - not sure out of ntfs */
2281
    // time_t    st_ctime;   /* time of last status change - not sure out of ntfs */
2282
2283
0
    std::string fpn;
2284
2285
    // FIXME: so the condition is always true?
2286
0
    if (vInfos->fileType.isFile() || vInfos->fileType.isLinkToUnknown() || vInfos->fileType.isDir()) {
2287
0
        fpn = vInfos->filePath + IGFD::Utils::GetPathSeparator() + vInfos->fileNameExt;
2288
0
    }
2289
2290
0
    m_FileSystemPtr->GetFileDateAndSize(fpn, vInfos->fileType, vInfos->fileModifDate, vInfos->fileSize);
2291
2292
0
    if (!vInfos->fileType.isDir()) {
2293
0
        vInfos->formatedFileSize = IGFD::Utils::FormatFileSize(vInfos->fileSize);
2294
0
    }
2295
0
}
2296
2297
0
void IGFD::FileManager::m_RemoveFileNameInSelection(const std::string& vFileName) {
2298
0
    m_SelectedFileNames.erase(vFileName);
2299
2300
0
    if (m_SelectedFileNames.size() == 1) {
2301
0
        snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
2302
0
    } else {
2303
0
        snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", m_SelectedFileNames.size());
2304
0
    }
2305
0
}
2306
2307
0
void IGFD::FileManager::m_AddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName) {
2308
0
    if (vFileName == "." || vFileName == "..") {
2309
0
        return;
2310
0
    }
2311
0
    m_SelectedFileNames.emplace(vFileName);
2312
2313
0
    if (m_SelectedFileNames.size() == 1) {
2314
0
        snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%s", vFileName.c_str());
2315
0
    } else {
2316
0
        snprintf(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, "%zu files Selected", m_SelectedFileNames.size());
2317
0
    }
2318
2319
0
    if (vSetLastSelectionFileName) {
2320
0
        m_LastSelectedFileName = vFileName;
2321
0
    }
2322
0
}
2323
2324
0
void IGFD::FileManager::SetCurrentDir(const std::string& vPath) {
2325
0
    std::string path = vPath;
2326
#ifdef _IGFD_WIN_
2327
    if (fsRoot == path) path += IGFD::Utils::GetPathSeparator();
2328
#endif  // _IGFD_WIN_
2329
2330
0
    bool dir_opened = m_FileSystemPtr->IsDirectory(path);
2331
0
    if (!dir_opened) {
2332
0
        path       = ".";
2333
0
        dir_opened = m_FileSystemPtr->IsDirectory(path);
2334
0
    }
2335
0
    if (dir_opened) {
2336
#ifdef _IGFD_WIN_
2337
        DWORD numchar      = 0;
2338
        std::wstring wpath = IGFD::Utils::UTF8Decode(path);
2339
        numchar            = GetFullPathNameW(wpath.c_str(), 0, nullptr, nullptr);
2340
        std::wstring fpath(numchar, 0);
2341
        GetFullPathNameW(wpath.c_str(), numchar, (wchar_t*)fpath.data(), nullptr);
2342
        std::string real_path = IGFD::Utils::UTF8Encode(fpath);
2343
        while (real_path.back() == '\0') {  // for fix issue we can have with std::string concatenation.. if there is a \0 at end
2344
            real_path = real_path.substr(0, real_path.size() - 1U);
2345
        }
2346
        if (!real_path.empty())
2347
#elif defined(_IGFD_UNIX_)  // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2348
        char real_path[PATH_MAX];
2349
0
        char* numchar = realpath(path.c_str(), real_path);
2350
0
        if (numchar != nullptr)
2351
0
#endif                      // _IGFD_WIN_
2352
0
        {
2353
0
            m_CurrentPath = std::move(real_path);
2354
0
            if (m_CurrentPath.size() > 1 && m_CurrentPath[m_CurrentPath.size() - 1] == PATH_SEP) {
2355
0
                m_CurrentPath = m_CurrentPath.substr(0, m_CurrentPath.size() - 1);
2356
0
            }
2357
0
            IGFD::Utils::SetBuffer(inputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath);
2358
0
            m_CurrentPathDecomposition = IGFD::Utils::SplitStringToVector(m_CurrentPath, PATH_SEP, false);
2359
0
#ifdef _IGFD_UNIX_  // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2360
0
            m_CurrentPathDecomposition.insert(m_CurrentPathDecomposition.begin(), IGFD::Utils::GetPathSeparator());
2361
0
#endif  // _IGFD_UNIX_
2362
0
            if (!m_CurrentPathDecomposition.empty()) {
2363
#ifdef _IGFD_WIN_
2364
                fsRoot = m_CurrentPathDecomposition[0];
2365
#endif  // _IGFD_WIN_
2366
0
            }
2367
0
        }
2368
0
    }
2369
0
}
2370
2371
0
bool IGFD::FileManager::CreateDir(const std::string& vPath) {
2372
0
    if (!vPath.empty()) {
2373
0
        std::string path = m_CurrentPath + IGFD::Utils::GetPathSeparator() + vPath;
2374
0
        return m_FileSystemPtr->CreateDirectoryIfNotExist(path);
2375
0
    }
2376
0
    return false;
2377
0
}
2378
2379
0
std::string IGFD::FileManager::ComposeNewPath(std::vector<std::string>::iterator vIter) {
2380
0
    std::string res;
2381
2382
0
    while (true) {
2383
0
        if (!res.empty()) {
2384
#ifdef _IGFD_WIN_
2385
            res = *vIter + IGFD::Utils::GetPathSeparator() + res;
2386
#elif defined(_IGFD_UNIX_)  // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2387
0
            if (*vIter == fsRoot)
2388
0
                res = *vIter + res;
2389
0
            else
2390
0
                res = *vIter + PATH_SEP + res;
2391
0
#endif                      // _IGFD_WIN_
2392
0
        } else
2393
0
            res = *vIter;
2394
2395
0
        if (vIter == m_CurrentPathDecomposition.begin()) {
2396
0
#ifdef _IGFD_UNIX_  // _IGFD_UNIX_ is _IGFD_WIN_ or APPLE
2397
0
            if (res[0] != PATH_SEP) res = PATH_SEP + res;
2398
#else
2399
            if (res.back() != PATH_SEP) res.push_back(PATH_SEP);
2400
#endif  // defined(_IGFD_UNIX_)
2401
0
            break;
2402
0
        }
2403
2404
0
        --vIter;
2405
0
    }
2406
2407
0
    return res;
2408
0
}
2409
2410
0
bool IGFD::FileManager::SetPathOnParentDirectoryIfAny() {
2411
0
    if (m_CurrentPathDecomposition.size() > 1) {
2412
0
        m_CurrentPath = ComposeNewPath(m_CurrentPathDecomposition.end() - 2);
2413
0
        return true;
2414
0
    }
2415
0
    return false;
2416
0
}
2417
2418
0
std::string IGFD::FileManager::GetCurrentPath() {
2419
0
    if (m_CurrentPath.empty()) {
2420
0
        m_CurrentPath = ".";
2421
0
    }
2422
0
    return m_CurrentPath;
2423
0
}
2424
2425
0
void IGFD::FileManager::SetCurrentPath(const std::string& vCurrentPath) {
2426
0
    if (vCurrentPath.empty())
2427
0
        m_CurrentPath = ".";
2428
0
    else
2429
0
        m_CurrentPath = vCurrentPath;
2430
0
}
2431
2432
0
void IGFD::FileManager::SetDefaultFileName(const std::string& vFileName) {
2433
0
    dLGDefaultFileName = vFileName;
2434
0
    IGFD::Utils::SetBuffer(fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFileName);
2435
0
}
2436
2437
0
bool IGFD::FileManager::SelectDirectory(const std::shared_ptr<FileInfos>& vInfos) {
2438
0
    if (!vInfos.use_count()) return false;
2439
2440
0
    bool pathClick = false;
2441
2442
0
    if (vInfos->fileNameExt == "..") {
2443
0
        pathClick = SetPathOnParentDirectoryIfAny();
2444
0
    } else {
2445
0
        std::string newPath;
2446
2447
0
        if (showDevices) {
2448
0
            newPath = vInfos->fileNameExt + IGFD::Utils::GetPathSeparator();
2449
0
        } else {
2450
#ifdef __linux__
2451
            if (fsRoot == m_CurrentPath)
2452
                newPath = m_CurrentPath + vInfos->fileNameExt;
2453
            else
2454
#endif  // __linux__
2455
0
                newPath = m_CurrentPath + IGFD::Utils::GetPathSeparator() + vInfos->fileNameExt;
2456
0
        }
2457
2458
0
        if (m_FileSystemPtr->IsDirectoryCanBeOpened(newPath)) {
2459
0
            if (showDevices) {
2460
0
                m_CurrentPath = vInfos->fileNameExt;
2461
0
                fsRoot        = m_CurrentPath;
2462
0
            } else {
2463
0
                m_CurrentPath = newPath;  //-V820
2464
0
            }
2465
0
            pathClick = true;
2466
0
        }
2467
0
    }
2468
2469
0
    return pathClick;
2470
0
}
2471
2472
0
void IGFD::FileManager::SelectAllFileNames() {
2473
0
    m_SelectedFileNames.clear();
2474
0
    for (const auto& pInfos : m_FilteredFileList) {
2475
0
        if (pInfos != nullptr) {
2476
0
            m_AddFileNameInSelection(pInfos->fileNameExt, true);
2477
0
        }
2478
0
    }
2479
0
}
2480
2481
0
void IGFD::FileManager::SelectFileName(const std::shared_ptr<FileInfos>& vInfos) {
2482
0
    if (!vInfos.use_count()) {
2483
0
        return;
2484
0
    }
2485
0
    m_AddFileNameInSelection(vInfos->fileNameExt, true);
2486
0
}
2487
2488
0
void IGFD::FileManager::SelectOrDeselectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos) {
2489
0
    if (!vInfos.use_count()) {
2490
0
        return;
2491
0
    }
2492
2493
0
    if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) {
2494
0
        if (dLGcountSelectionMax == 0) {                                                       // infinite selection
2495
0
            if (m_SelectedFileNames.find(vInfos->fileNameExt) == m_SelectedFileNames.end()) {  // not found +> add
2496
0
                m_AddFileNameInSelection(vInfos->fileNameExt, true);
2497
0
            } else {  // found +> remove
2498
0
                m_RemoveFileNameInSelection(vInfos->fileNameExt);
2499
0
            }
2500
0
        } else {  // selection limited by size
2501
0
            if (m_SelectedFileNames.size() < dLGcountSelectionMax) {
2502
0
                if (m_SelectedFileNames.find(vInfos->fileNameExt) == m_SelectedFileNames.end()) {  // not found +> add
2503
0
                    m_AddFileNameInSelection(vInfos->fileNameExt, true);
2504
0
                } else {  // found +> remove
2505
0
                    m_RemoveFileNameInSelection(vInfos->fileNameExt);
2506
0
                }
2507
0
            }
2508
0
        }
2509
0
    } else if (ImGui::IsKeyDown(ImGuiMod_Shift)) {
2510
0
        if (dLGcountSelectionMax != 1) {
2511
0
            m_SelectedFileNames.clear();
2512
            // we will iterate filelist and get the last selection after the start selection
2513
0
            bool startMultiSelection     = false;
2514
0
            std::string fileNameToSelect = vInfos->fileNameExt;
2515
0
            std::string savedLastSelectedFileName;  // for invert selection mode
2516
0
            for (const auto& file : m_FileList) {
2517
0
                if (!file.use_count()) {
2518
0
                    continue;
2519
0
                }
2520
0
                bool canTake = true;
2521
0
                if (!file->SearchForTag(vFileDialogInternal.searchManager.searchTag)) canTake = false;
2522
0
                if (canTake) {  // if not filtered, we will take files who are filtered by the dialog
2523
0
                    if (file->fileNameExt == m_LastSelectedFileName) {
2524
0
                        startMultiSelection = true;
2525
0
                        m_AddFileNameInSelection(m_LastSelectedFileName, false);
2526
0
                    } else if (startMultiSelection) {
2527
0
                        if (dLGcountSelectionMax == 0) {  // infinite selection
2528
0
                            m_AddFileNameInSelection(file->fileNameExt, false);
2529
0
                        } else {  // selection limited by size
2530
0
                            if (m_SelectedFileNames.size() < dLGcountSelectionMax) {
2531
0
                                m_AddFileNameInSelection(file->fileNameExt, false);
2532
0
                            } else {
2533
0
                                startMultiSelection = false;
2534
0
                                if (!savedLastSelectedFileName.empty()) m_LastSelectedFileName = savedLastSelectedFileName;
2535
0
                                break;
2536
0
                            }
2537
0
                        }
2538
0
                    }
2539
2540
0
                    if (file->fileNameExt == fileNameToSelect) {
2541
0
                        if (!startMultiSelection) {  // we are before the last Selected FileName, so we must inverse
2542
0
                            savedLastSelectedFileName = m_LastSelectedFileName;
2543
0
                            m_LastSelectedFileName    = fileNameToSelect;
2544
0
                            fileNameToSelect          = savedLastSelectedFileName;
2545
0
                            startMultiSelection       = true;
2546
0
                            m_AddFileNameInSelection(m_LastSelectedFileName, false);
2547
0
                        } else {
2548
0
                            startMultiSelection = false;
2549
0
                            if (!savedLastSelectedFileName.empty()) m_LastSelectedFileName = savedLastSelectedFileName;
2550
0
                            break;
2551
0
                        }
2552
0
                    }
2553
0
                }
2554
0
            }
2555
0
        }
2556
0
    } else {
2557
0
        m_SelectedFileNames.clear();
2558
0
        IGFD::Utils::ResetBuffer(fileNameBuffer);
2559
0
        m_AddFileNameInSelection(vInfos->fileNameExt, true);
2560
0
    }
2561
0
}
2562
2563
0
void IGFD::FileManager::DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal) {
2564
0
    if (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableCreateDirectoryButton) return;
2565
2566
0
    if (IMGUI_BUTTON(createDirButtonString)) {
2567
0
        if (!m_CreateDirectoryMode) {
2568
0
            m_CreateDirectoryMode = true;
2569
0
            IGFD::Utils::ResetBuffer(directoryNameBuffer);
2570
0
        }
2571
0
    }
2572
0
    if (ImGui::IsItemHovered()) ImGui::SetTooltip(buttonCreateDirString);
2573
2574
0
    if (m_CreateDirectoryMode) {
2575
0
        ImGui::SameLine();
2576
2577
0
        ImGui::PushItemWidth(100.0f);
2578
0
        ImGui::InputText("##DirectoryFileName", directoryNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER);
2579
0
        ImGui::PopItemWidth();
2580
2581
0
        ImGui::SameLine();
2582
2583
0
        if (IMGUI_BUTTON(okButtonString)) {
2584
0
            std::string newDir = std::string(directoryNameBuffer);
2585
0
            if (CreateDir(newDir)) {
2586
0
                SetCurrentPath(m_CurrentPath + IGFD::Utils::GetPathSeparator() + newDir);
2587
0
                OpenCurrentPath(vFileDialogInternal);
2588
0
            }
2589
2590
0
            m_CreateDirectoryMode = false;
2591
0
        }
2592
2593
0
        ImGui::SameLine();
2594
2595
0
        if (IMGUI_BUTTON(cancelButtonString)) {
2596
0
            m_CreateDirectoryMode = false;
2597
0
        }
2598
0
    }
2599
2600
0
    ImGui::SameLine();
2601
0
}
2602
2603
0
void IGFD::FileManager::DrawPathComposer(const FileDialogInternal& vFileDialogInternal) {
2604
0
    if (IMGUI_BUTTON(resetButtonString)) {
2605
0
        SetCurrentPath(".");
2606
0
        OpenCurrentPath(vFileDialogInternal);
2607
0
    }
2608
0
    if (ImGui::IsItemHovered()) {
2609
0
        ImGui::SetTooltip(buttonResetPathString);
2610
0
    }
2611
0
    if (vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ShowDevicesButton) {
2612
0
        ImGui::SameLine();
2613
0
        if (IMGUI_BUTTON(devicesButtonString)) {
2614
0
            devicesClicked = true;
2615
0
        }
2616
0
        if (ImGui::IsItemHovered()) {
2617
0
            ImGui::SetTooltip(buttonDriveString);
2618
0
        }
2619
0
    }
2620
2621
0
    ImGui::SameLine();
2622
2623
0
    if (IMGUI_BUTTON(editPathButtonString)) {
2624
0
        inputPathActivated = !inputPathActivated;
2625
0
        if (inputPathActivated) {
2626
0
            if (!m_CurrentPathDecomposition.empty()) {
2627
0
                auto endIt    = m_CurrentPathDecomposition.end();
2628
0
                m_CurrentPath = ComposeNewPath(--endIt);
2629
0
                IGFD::Utils::SetBuffer(inputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath);
2630
0
            }
2631
0
        }
2632
0
    }
2633
0
    if (ImGui::IsItemHovered()) {
2634
0
        ImGui::SetTooltip(buttonEditPathString);
2635
0
    }
2636
2637
0
    ImGui::SameLine();
2638
2639
0
    ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
2640
2641
    // show current path
2642
0
    if (!m_CurrentPathDecomposition.empty()) {
2643
0
        ImGui::SameLine();
2644
2645
0
        if (inputPathActivated) {
2646
0
            ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
2647
0
            ImGui::InputText("##pathedition", inputPathBuffer, MAX_PATH_BUFFER_SIZE);
2648
0
            ImGui::PopItemWidth();
2649
0
        } else {
2650
0
            int _id = 0;
2651
0
            for (auto itPathDecomp = m_CurrentPathDecomposition.begin(); itPathDecomp != m_CurrentPathDecomposition.end(); ++itPathDecomp) {
2652
0
                if (itPathDecomp != m_CurrentPathDecomposition.begin()) {
2653
#if defined(CUSTOM_PATH_SPACING)
2654
                    ImGui::SameLine(0, CUSTOM_PATH_SPACING);
2655
#else
2656
0
                    ImGui::SameLine();
2657
0
#endif  // USE_CUSTOM_PATH_SPACING
2658
0
                    if (!(vFileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableQuickPathSelection)) {
2659
#if defined(_IGFD_WIN_)
2660
                        const char* sep = "\\";
2661
#elif defined(_IGFD_UNIX_)
2662
                        const char* sep = "/";
2663
0
                        if (itPathDecomp != m_CurrentPathDecomposition.begin() + 1)
2664
0
#endif
2665
0
                        {
2666
0
                            ImGui::PushID(_id++);
2667
0
                            bool click = IMGUI_PATH_BUTTON(sep);
2668
0
                            ImGui::PopID();
2669
2670
#if defined(CUSTOM_PATH_SPACING)
2671
                            ImGui::SameLine(0, CUSTOM_PATH_SPACING);
2672
#else
2673
0
                            ImGui::SameLine();
2674
0
#endif  // USE_CUSTOM_PATH_SPACING
2675
2676
0
                            if (click) {
2677
0
                                m_OpenPathPopup(vFileDialogInternal, itPathDecomp - 1);
2678
0
                            } else if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
2679
0
                                m_SetCurrentPath(itPathDecomp - 1);
2680
0
                                break;
2681
0
                            }
2682
0
                        }
2683
0
                    }
2684
0
                }
2685
2686
0
                ImGui::PushID(_id++);
2687
0
                bool click = IMGUI_PATH_BUTTON((*itPathDecomp).c_str());
2688
0
                ImGui::PopID();
2689
0
                if (click) {
2690
0
                    m_CurrentPath = ComposeNewPath(itPathDecomp);
2691
0
                    pathClicked   = true;
2692
0
                    break;
2693
0
                } else if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {  // activate input for path
2694
0
                    m_SetCurrentPath(itPathDecomp);
2695
0
                    break;
2696
0
                }
2697
0
            }
2698
0
        }
2699
0
    }
2700
0
}
2701
2702
0
void IGFD::FileManager::m_SetCurrentPath(std::vector<std::string>::iterator vPathIter) {
2703
0
    m_CurrentPath = ComposeNewPath(vPathIter);
2704
0
    IGFD::Utils::SetBuffer(inputPathBuffer, MAX_PATH_BUFFER_SIZE, m_CurrentPath);
2705
0
    inputPathActivated = true;
2706
0
}
2707
2708
0
std::string IGFD::FileManager::GetResultingPath() {
2709
0
    if (dLGDirectoryMode && m_SelectedFileNames.size() == 1) {  // if directory mode with selection 1
2710
0
        std::string selectedDirectory = fileNameBuffer;
2711
0
        std::string path              = m_CurrentPath;
2712
0
        if (!selectedDirectory.empty() && selectedDirectory != ".") {
2713
0
            path += IGFD::Utils::GetPathSeparator() + selectedDirectory;
2714
0
        }
2715
0
        return path;
2716
0
    }
2717
0
    return m_CurrentPath;  // if file mode
2718
0
}
2719
2720
0
std::string IGFD::FileManager::GetResultingFileName(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag) {
2721
0
    if (!dLGDirectoryMode) {  // if not directory mode
2722
0
        const auto& filename = std::string(fileNameBuffer);
2723
0
        return vFileDialogInternal.filterManager.ReplaceExtentionWithCurrentFilterIfNeeded(filename, vFlag);
2724
0
    }
2725
0
    return "";  // directory mode
2726
0
}
2727
2728
0
std::string IGFD::FileManager::GetResultingFilePathName(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag) {
2729
0
    if (!dLGDirectoryMode) {  // if not directory mode
2730
0
        auto result                = GetResultingPath();
2731
0
        const auto& file_path_name = GetResultingFileName(vFileDialogInternal, vFlag);
2732
0
        if (!file_path_name.empty()) {
2733
0
            if (m_FileSystemPtr != nullptr && file_path_name.find(IGFD::Utils::GetPathSeparator()) != std::string::npos &&  // check if a path
2734
0
                m_FileSystemPtr->IsFileExist(file_path_name)) {                                                             // do that only if filename is a path, not only a file name
2735
0
                result = file_path_name;                                                                                    // #144, exist file, so absolute, so return it (maybe set by user in inputText)
2736
0
            } else {                                                                                                        // #144, else concate path with current filename
2737
0
#ifdef _IGFD_UNIX_
2738
0
                if (fsRoot != result)
2739
0
#endif  // _IGFD_UNIX_
2740
0
                {
2741
0
                    result += IGFD::Utils::GetPathSeparator();
2742
0
                }
2743
0
                result += file_path_name;
2744
0
            }
2745
0
        }
2746
2747
0
        return result;  // file mode
2748
0
    }
2749
0
    return "";  // directory mode
2750
0
}
2751
2752
0
std::map<std::string, std::string> IGFD::FileManager::GetResultingSelection(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag) {
2753
0
    std::map<std::string, std::string> res;
2754
0
    const auto& result_path = GetResultingPath();
2755
0
    if (!m_SelectedFileNames.empty()) {
2756
0
        for (const auto& selectedFileName : m_SelectedFileNames) {
2757
0
            auto result = result_path;
2758
0
#ifdef _IGFD_UNIX_
2759
0
            if (fsRoot != result)
2760
0
#endif  // _IGFD_UNIX_
2761
0
            {
2762
0
                result += IGFD::Utils::GetPathSeparator();
2763
0
            }
2764
0
            result += vFileDialogInternal.filterManager.ReplaceExtentionWithCurrentFilterIfNeeded(selectedFileName, vFlag);
2765
0
            res[selectedFileName] = result;
2766
0
        }
2767
0
    } else {                                                     // opened directory with no selection
2768
0
        if (vFileDialogInternal.fileManager.dLGDirectoryMode) {  // directory mode
2769
0
            res["."] = result_path;
2770
0
        }
2771
0
    }
2772
0
    return res;
2773
0
}
2774
2775
0
void IGFD::FileDialogInternal::NewFrame() {
2776
0
    canWeContinue              = true;   // reset flag for possibily validate the dialog
2777
0
    isOk                       = false;  // reset dialog result
2778
0
    fileManager.devicesClicked = false;
2779
0
    fileManager.pathClicked    = false;
2780
2781
0
    needToExitDialog = false;
2782
2783
#ifdef USE_DIALOG_EXIT_WITH_KEY
2784
    if (ImGui::IsKeyPressed(IGFD_EXIT_KEY)) {
2785
        // we do that here with the data's defined at the last frame
2786
        // because escape key can quit input activation and at the end of the frame all flag will be false
2787
        // so we will detect nothing
2788
        if (!(fileManager.inputPathActivated || searchManager.searchInputIsActive || fileInputIsActive || fileListViewIsActive)) {
2789
            needToExitDialog = true;  // need to quit dialog
2790
        }
2791
    } else
2792
#endif
2793
0
    {
2794
0
        searchManager.searchInputIsActive = false;
2795
0
        fileInputIsActive                 = false;
2796
0
        fileListViewIsActive              = false;
2797
0
    }
2798
0
}
2799
2800
0
void IGFD::FileDialogInternal::EndFrame() {
2801
    // directory change
2802
0
    if (fileManager.pathClicked) {
2803
0
        fileManager.OpenCurrentPath(*this);
2804
0
    }
2805
2806
0
    if (fileManager.devicesClicked) {
2807
0
        if (fileManager.GetDevices()) {
2808
0
            fileManager.ApplyFilteringOnFileList(*this);
2809
0
        }
2810
0
    }
2811
2812
0
    if (fileManager.inputPathActivated) {
2813
0
        auto gio = ImGui::GetIO();
2814
0
        if (ImGui::IsKeyReleased(ImGuiKey_Enter)) {
2815
0
            fileManager.SetCurrentPath(std::string(fileManager.inputPathBuffer));
2816
0
            fileManager.OpenCurrentPath(*this);
2817
0
            fileManager.inputPathActivated = false;
2818
0
        }
2819
0
        if (ImGui::IsKeyReleased(ImGuiKey_Escape)) {
2820
0
            fileManager.inputPathActivated = false;
2821
0
        }
2822
0
    }
2823
2824
0
    if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) {
2825
0
        if (ImGui::IsKeyDown(SelectAllFilesKey)) {
2826
0
            fileManager.SelectAllFileNames();
2827
0
        }
2828
0
    }
2829
0
}
2830
2831
0
void IGFD::FileDialogInternal::ResetForNewDialog() {
2832
0
}
2833
2834
0
void IGFD::FileDialogInternal::configureDialog(const std::string& vKey, const std::string& vTitle, const char* vFilters, const FileDialogConfig& vConfig) {
2835
0
    m_DialogConfig = vConfig;
2836
0
    ResetForNewDialog();
2837
0
    dLGkey   = vKey;
2838
0
    dLGtitle = vTitle;
2839
2840
    // treatment
2841
0
    if (m_DialogConfig.sidePane == nullptr) {
2842
0
        m_DialogConfig.sidePaneWidth = 0.0f;
2843
0
    }
2844
2845
0
    if (m_DialogConfig.filePathName.empty()) {
2846
0
        if (m_DialogConfig.path.empty()) {
2847
0
            fileManager.dLGpath = fileManager.GetCurrentPath();
2848
0
        } else {
2849
0
            fileManager.dLGpath = m_DialogConfig.path;
2850
0
        }
2851
0
        fileManager.SetCurrentPath(m_DialogConfig.path);
2852
0
        fileManager.dLGcountSelectionMax = (size_t)m_DialogConfig.countSelectionMax;
2853
0
        fileManager.SetDefaultFileName(m_DialogConfig.fileName);
2854
0
    } else {
2855
0
        auto ps = fileManager.GetFileSystemInstance()->ParsePathFileName(m_DialogConfig.filePathName);
2856
0
        if (ps.isOk) {
2857
0
            fileManager.dLGpath = ps.path;
2858
0
            fileManager.SetDefaultFileName(ps.name);
2859
0
            filterManager.dLGdefaultExt = "." + ps.ext;
2860
0
        } else {
2861
0
            fileManager.dLGpath = fileManager.GetCurrentPath();
2862
0
            fileManager.SetDefaultFileName("");
2863
0
            filterManager.dLGdefaultExt.clear();
2864
0
        }
2865
0
    }
2866
2867
0
    filterManager.dLGdefaultExt.clear();
2868
0
    filterManager.ParseFilters(vFilters);
2869
0
    filterManager.SetSelectedFilterWithExt(filterManager.dLGdefaultExt);
2870
0
    fileManager.SetCurrentPath(fileManager.dLGpath);
2871
0
    fileManager.dLGDirectoryMode     = (vFilters == nullptr);
2872
0
    fileManager.dLGcountSelectionMax = m_DialogConfig.countSelectionMax;  //-V101
2873
0
    fileManager.ClearAll();
2874
0
    showDialog = true;
2875
0
}
2876
2877
0
const IGFD::FileDialogConfig& IGFD::FileDialogInternal::getDialogConfig() const {
2878
0
    return m_DialogConfig;
2879
0
}
2880
2881
0
IGFD::FileDialogConfig& IGFD::FileDialogInternal::getDialogConfigRef() {
2882
0
    return m_DialogConfig;
2883
0
}
2884
2885
1
IGFD::ThumbnailFeature::ThumbnailFeature() {
2886
#ifdef USE_THUMBNAILS
2887
    m_DisplayMode = DisplayModeEnum::FILE_LIST;
2888
#endif
2889
1
}
2890
2891
1
IGFD::ThumbnailFeature::~ThumbnailFeature() = default;
2892
2893
0
void IGFD::ThumbnailFeature::m_NewThumbnailFrame(FileDialogInternal& /*vFileDialogInternal*/) {
2894
#ifdef USE_THUMBNAILS
2895
    m_StartThumbnailFileDatasExtraction();
2896
#endif
2897
0
}
2898
2899
0
void IGFD::ThumbnailFeature::m_EndThumbnailFrame(FileDialogInternal& vFileDialogInternal) {
2900
#ifdef USE_THUMBNAILS
2901
    m_ClearThumbnails(vFileDialogInternal);
2902
#else
2903
0
    (void)vFileDialogInternal;
2904
0
#endif
2905
0
}
2906
2907
0
void IGFD::ThumbnailFeature::m_QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal) {
2908
#ifdef USE_THUMBNAILS
2909
    m_StopThumbnailFileDatasExtraction();
2910
    m_ClearThumbnails(vFileDialogInternal);
2911
#else
2912
0
    (void)vFileDialogInternal;
2913
0
#endif
2914
0
}
2915
2916
#ifdef USE_THUMBNAILS
2917
void IGFD::ThumbnailFeature::m_StartThumbnailFileDatasExtraction() {
2918
    const bool res = m_ThumbnailGenerationThread.use_count() && m_ThumbnailGenerationThread->joinable();
2919
    if (!res) {
2920
        m_IsWorking                 = true;
2921
        m_CountFiles                = 0U;
2922
        m_ThumbnailGenerationThread = std::shared_ptr<std::thread>(new std::thread(&IGFD::ThumbnailFeature::m_ThreadThumbnailFileDatasExtractionFunc, this), [this](std::thread* obj_ptr) {
2923
            m_IsWorking = false;
2924
            if (obj_ptr != nullptr) {
2925
                m_ThumbnailFileDatasToGetCv.notify_all();
2926
                obj_ptr->join();
2927
            }
2928
        });
2929
    }
2930
}
2931
2932
bool IGFD::ThumbnailFeature::m_StopThumbnailFileDatasExtraction() {
2933
    const bool res = m_ThumbnailGenerationThread.use_count() && m_ThumbnailGenerationThread->joinable();
2934
    if (res) {
2935
        m_ThumbnailGenerationThread.reset();
2936
    }
2937
    return res;
2938
}
2939
2940
void IGFD::ThumbnailFeature::m_ThreadThumbnailFileDatasExtractionFunc() {
2941
    m_CountFiles = 0U;
2942
    m_IsWorking  = true;
2943
    // infinite loop while is thread working
2944
    while (m_IsWorking) {
2945
        std::unique_lock<std::mutex> thumbnailFileDatasToGetLock(m_ThumbnailFileDatasToGetMutex);
2946
        m_ThumbnailFileDatasToGetCv.wait(thumbnailFileDatasToGetLock);
2947
        if (!m_ThumbnailFileDatasToGet.empty()) {
2948
            std::shared_ptr<FileInfos> file = nullptr;
2949
            // get the first file in the list
2950
            file = (*m_ThumbnailFileDatasToGet.begin());
2951
            m_ThumbnailFileDatasToGet.pop_front();
2952
            thumbnailFileDatasToGetLock.unlock();
2953
            // retrieve datas of the texture file if its an image file
2954
            if (file.use_count()) {
2955
                if (file->fileType.isFile()) {  //-V522
2956
                    //|| file->fileExtLevels == ".hdr" => format float so in few times
2957
                    if (file->SearchForExts(".png,.bmp,.tga,.jpg,.jpeg,.gif,.psd,.pic,.ppm,.pgm", true)) {
2958
                        auto fpn       = file->filePath + IGFD::Utils::GetPathSeparator() + file->fileNameExt;
2959
                        int w          = 0;
2960
                        int h          = 0;
2961
                        int chans      = 0;
2962
                        uint8_t* datas = stbi_load(fpn.c_str(), &w, &h, &chans, STBI_rgb_alpha);
2963
                        if (datas != nullptr) {
2964
                            if (w != 0 && h != 0) {
2965
                                // resize with respect to glyph ratio
2966
                                const float ratioX = (float)w / (float)h;
2967
                                const float newX   = DisplayMode_ThumbailsList_ImageHeight * ratioX;
2968
                                float newY         = w / ratioX;
2969
                                if (newX < w) {
2970
                                    newY = DisplayMode_ThumbailsList_ImageHeight;
2971
                                }
2972
                                const auto newWidth         = (int)newX;
2973
                                const auto newHeight        = (int)newY;
2974
                                const auto newBufSize       = (size_t)(newWidth * newHeight * 4U);  //-V112 //-V1028
2975
                                auto resizedData            = new uint8_t[newBufSize];
2976
                                const auto* resizeSucceeded = stbir_resize_uint8_linear(datas, w, h, 0, resizedData, newWidth, newHeight, 0, stbir_pixel_layout::STBIR_RGBA);  //-V112
2977
                                if (resizeSucceeded != nullptr) {
2978
                                    auto th              = &file->thumbnailInfo;
2979
                                    th->textureFileDatas = resizedData;
2980
                                    th->textureWidth     = newWidth;
2981
                                    th->textureHeight    = newHeight;
2982
                                    th->textureChannels  = 4;  //-V112
2983
                                    // we set that at least, because will launch the gpu creation of the texture in the
2984
                                    // main thread
2985
                                    th->isReadyToUpload = true;
2986
                                    // need gpu loading
2987
                                    m_AddThumbnailToCreate(file);
2988
                                } else {
2989
                                    delete[] resizedData;
2990
                                }
2991
                            } else {
2992
                                printf("image loading fail : w:%i h:%i c:%i\n", w, h, 4);  //-V112
2993
                            }
2994
                            stbi_image_free(datas);
2995
                        }
2996
                    }
2997
                }
2998
            }
2999
        } else {
3000
            thumbnailFileDatasToGetLock.unlock();
3001
        }
3002
    }
3003
}
3004
3005
void IGFD::ThumbnailFeature::m_VariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...) {
3006
    va_list args;
3007
    va_start(args, fmt);
3008
    char TempBuffer[512];
3009
    const int w = vsnprintf(TempBuffer, 511, fmt, args);
3010
    va_end(args);
3011
    if (w) {
3012
        ImGui::ProgressBar(fraction, size_arg, TempBuffer);
3013
    }
3014
}
3015
3016
void IGFD::ThumbnailFeature::m_DrawThumbnailGenerationProgress() {
3017
    if (m_ThumbnailGenerationThread.use_count() && m_ThumbnailGenerationThread->joinable()) {
3018
        m_ThumbnailFileDatasToGetMutex.lock();
3019
        if (!m_ThumbnailFileDatasToGet.empty()) {
3020
            const auto p = (float)((double)m_CountFiles / (double)m_ThumbnailFileDatasToGet.size());                     // read => no thread concurency issues
3021
            m_VariadicProgressBar(p, ImVec2(50, 0), "%u/%u", m_CountFiles, (uint32_t)m_ThumbnailFileDatasToGet.size());  // read => no thread concurency issues
3022
            ImGui::SameLine();
3023
        }
3024
        m_ThumbnailFileDatasToGetMutex.unlock();
3025
        m_ThumbnailFileDatasToGetCv.notify_all();
3026
    }
3027
}
3028
3029
void IGFD::ThumbnailFeature::m_AddThumbnailToLoad(const std::shared_ptr<FileInfos>& vFileInfos) {
3030
    if (vFileInfos.use_count()) {
3031
        if (vFileInfos->fileType.isFile()) {
3032
            //|| file->fileExtLevels == ".hdr" => format float so in few times
3033
            if (vFileInfos->SearchForExts(".png,.bmp,.tga,.jpg,.jpeg,.gif,.psd,.pic,.ppm,.pgm", true)) {
3034
                // write => thread concurency issues
3035
                m_ThumbnailFileDatasToGetMutex.lock();
3036
                m_ThumbnailFileDatasToGet.push_back(vFileInfos);
3037
                vFileInfos->thumbnailInfo.isLoadingOrLoaded = true;
3038
                m_ThumbnailFileDatasToGetMutex.unlock();
3039
            }
3040
            m_ThumbnailFileDatasToGetCv.notify_all();
3041
        }
3042
    }
3043
}
3044
3045
void IGFD::ThumbnailFeature::m_AddThumbnailToCreate(const std::shared_ptr<FileInfos>& vFileInfos) {
3046
    if (vFileInfos.use_count()) {
3047
        // write => thread concurency issues
3048
        m_ThumbnailToCreateMutex.lock();
3049
        m_ThumbnailToCreate.push_back(vFileInfos);
3050
        m_ThumbnailToCreateMutex.unlock();
3051
    }
3052
}
3053
3054
void IGFD::ThumbnailFeature::m_AddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info) {
3055
    // write => thread concurency issues
3056
    m_ThumbnailToDestroyMutex.lock();
3057
    m_ThumbnailToDestroy.push_back(vIGFD_Thumbnail_Info);
3058
    m_ThumbnailToDestroyMutex.unlock();
3059
}
3060
3061
void IGFD::ThumbnailFeature::m_DrawDisplayModeToolBar() {
3062
    if (IMGUI_RADIO_BUTTON(DisplayMode_FilesList_ButtonString, m_DisplayMode == DisplayModeEnum::FILE_LIST)) m_DisplayMode = DisplayModeEnum::FILE_LIST;
3063
    if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_FilesList_ButtonHelp);
3064
    ImGui::SameLine();
3065
    if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsList_ButtonString, m_DisplayMode == DisplayModeEnum::THUMBNAILS_LIST)) m_DisplayMode = DisplayModeEnum::THUMBNAILS_LIST;
3066
    if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsList_ButtonHelp);
3067
    ImGui::SameLine();
3068
    /* todo
3069
    if (IMGUI_RADIO_BUTTON(DisplayMode_ThumbailsGrid_ButtonString,
3070
        m_DisplayMode == DisplayModeEnum::THUMBNAILS_GRID))
3071
        m_DisplayMode = DisplayModeEnum::THUMBNAILS_GRID;
3072
    if (ImGui::IsItemHovered()) ImGui::SetTooltip(DisplayMode_ThumbailsGrid_ButtonHelp);
3073
    ImGui::SameLine();
3074
    */
3075
    m_DrawThumbnailGenerationProgress();
3076
}
3077
3078
void IGFD::ThumbnailFeature::m_ClearThumbnails(FileDialogInternal& vFileDialogInternal) {
3079
    // directory wil be changed so the file list will be erased
3080
    if (vFileDialogInternal.fileManager.pathClicked) {
3081
        size_t count = vFileDialogInternal.fileManager.GetFullFileListSize();
3082
        for (size_t idx = 0U; idx < count; idx++) {
3083
            auto file = vFileDialogInternal.fileManager.GetFullFileAt(idx);
3084
            if (file.use_count()) {
3085
                if (file->thumbnailInfo.isReadyToDisplay)  //-V522
3086
                {
3087
                    m_AddThumbnailToDestroy(file->thumbnailInfo);
3088
                }
3089
            }
3090
        }
3091
    }
3092
}
3093
3094
void IGFD::ThumbnailFeature::SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun) {
3095
    m_CreateThumbnailFun = vCreateThumbnailFun;
3096
}
3097
3098
void IGFD::ThumbnailFeature::SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun) {
3099
    m_DestroyThumbnailFun = vCreateThumbnailFun;
3100
}
3101
3102
void IGFD::ThumbnailFeature::ManageGPUThumbnails() {
3103
    if (m_CreateThumbnailFun) {
3104
        m_ThumbnailToCreateMutex.lock();
3105
        if (!m_ThumbnailToCreate.empty()) {
3106
            for (const auto& file : m_ThumbnailToCreate) {
3107
                if (file.use_count()) {
3108
                    m_CreateThumbnailFun(&file->thumbnailInfo);
3109
                }
3110
            }
3111
            m_ThumbnailToCreate.clear();
3112
        }
3113
        m_ThumbnailToCreateMutex.unlock();
3114
    } else {
3115
        printf(
3116
            "No Callback found for create texture\nYou need to define the callback with a call to "
3117
            "SetCreateThumbnailCallback\n");
3118
    }
3119
3120
    if (m_DestroyThumbnailFun) {
3121
        m_ThumbnailToDestroyMutex.lock();
3122
        if (!m_ThumbnailToDestroy.empty()) {
3123
            for (auto thumbnail : m_ThumbnailToDestroy) {
3124
                m_DestroyThumbnailFun(&thumbnail);
3125
            }
3126
            m_ThumbnailToDestroy.clear();
3127
        }
3128
        m_ThumbnailToDestroyMutex.unlock();
3129
    } else {
3130
        printf(
3131
            "No Callback found for destroy texture\nYou need to define the callback with a call to "
3132
            "SetCreateThumbnailCallback\n");
3133
    }
3134
}
3135
3136
#endif  // USE_THUMBNAILS
3137
3138
1
IGFD::PlacesFeature::PlacesFeature() {
3139
#ifdef USE_PLACES_FEATURE
3140
    m_PlacesPaneWidth = defaultPlacePaneWith;
3141
    m_PlacesPaneShown = PLACES_PANE_DEFAULT_SHOWN;
3142
#endif  // USE_PLACES_FEATURE
3143
1
}
3144
3145
#ifdef USE_PLACES_FEATURE
3146
void IGFD::PlacesFeature::m_InitPlaces(FileDialogInternal& vFileDialogInternal) {
3147
#ifdef USE_PLACES_BOOKMARKS
3148
    (void)vFileDialogInternal;  // for disable compiler warning about unused var
3149
    AddPlacesGroup(placesBookmarksGroupName, placesBookmarksDisplayOrder, true, PLACES_BOOKMARK_DEFAULT_OPEPEND);
3150
#endif  // USE_PLACES_BOOKMARK
3151
#ifdef USE_PLACES_DEVICES
3152
    AddPlacesGroup(placesDevicesGroupName, placesDevicesDisplayOrder, false, PLACES_DEVICES_DEFAULT_OPEPEND);
3153
    auto devices_ptr = GetPlacesGroupPtr(placesDevicesGroupName);
3154
    if (devices_ptr != nullptr && vFileDialogInternal.fileManager.GetFileSystemInstance() != nullptr) {
3155
        const auto& devices = vFileDialogInternal.fileManager.GetFileSystemInstance()->GetDevicesList();
3156
        for (const auto& device : devices) {
3157
            devices_ptr->AddPlace(device.first + " " + device.second, device.first + IGFD::Utils::GetPathSeparator(), false);
3158
        }
3159
        devices_ptr = nullptr;
3160
    }
3161
#endif  // USE_PLACES_DEVICES
3162
}
3163
3164
void IGFD::PlacesFeature::m_DrawPlacesButton() {
3165
    IMGUI_TOGGLE_BUTTON(placesButtonString, &m_PlacesPaneShown);
3166
    if (ImGui::IsItemHovered()) ImGui::SetTooltip(placesButtonHelpString);
3167
}
3168
3169
bool IGFD::PlacesFeature::m_DrawPlacesPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize) {
3170
    bool res = false;
3171
    ImGui::BeginChild("##placespane", vSize);
3172
    for (const auto& group : m_OrderedGroups) {
3173
        auto group_ptr = group.second.lock();
3174
        if (group_ptr != nullptr) {
3175
            if (ImGui::CollapsingHeader(group_ptr->name.c_str(), group_ptr->collapsingHeaderFlag)) {
3176
                ImGui::BeginChild(group_ptr->name.c_str(), ImVec2(0, 0), ImGuiChildFlags_AutoResizeY);
3177
                if (group_ptr->canBeEdited) {
3178
                    ImGui::PushID(group_ptr.get());
3179
                    if (IMGUI_BUTTON(addPlaceButtonString "##ImGuiFileDialogAddPlace")) {
3180
                        if (!vFileDialogInternal.fileManager.IsComposerEmpty()) {
3181
                            group_ptr->AddPlace(vFileDialogInternal.fileManager.GetBack(), vFileDialogInternal.fileManager.GetCurrentPath(), true);
3182
                        }
3183
                    }
3184
                    if (group_ptr->selectedPlaceForEdition >= 0 && group_ptr->selectedPlaceForEdition < (int)group_ptr->places.size()) {
3185
                        ImGui::SameLine();
3186
                        if (IMGUI_BUTTON(removePlaceButtonString "##ImGuiFileDialogRemovePlace")) {
3187
                            group_ptr->places.erase(group_ptr->places.begin() + group_ptr->selectedPlaceForEdition);
3188
                            if (group_ptr->selectedPlaceForEdition == (int)group_ptr->places.size()) {
3189
                                --group_ptr->selectedPlaceForEdition;
3190
                            }
3191
                        }
3192
                        if (group_ptr->selectedPlaceForEdition >= 0 && group_ptr->selectedPlaceForEdition < (int)group_ptr->places.size()) {
3193
                            ImGui::SameLine();
3194
                            if (IMGUI_BUTTON(validatePlaceButtonString "##ImGuiFileDialogOkPlace")) {
3195
                                group_ptr->places[(size_t)group_ptr->selectedPlaceForEdition].name = std::string(group_ptr->editBuffer);
3196
                                group_ptr->selectedPlaceForEdition                                 = -1;
3197
                            }
3198
                            ImGui::SameLine();
3199
                            ImGui::PushItemWidth(vSize.x - ImGui::GetCursorPosX());
3200
                            if (ImGui::InputText("##ImGuiFileDialogPlaceEdit", group_ptr->editBuffer, MAX_FILE_DIALOG_NAME_BUFFER)) {
3201
                                group_ptr->places[(size_t)group_ptr->selectedPlaceForEdition].name = std::string(group_ptr->editBuffer);
3202
                            }
3203
                            ImGui::PopItemWidth();
3204
                        }
3205
                    }
3206
                    ImGui::PopID();
3207
                    ImGui::Separator();
3208
                }
3209
                if (!group_ptr->places.empty()) {
3210
                    const auto& current_path = vFileDialogInternal.fileManager.GetCurrentPath();
3211
                    group_ptr->clipper.Begin((int)group_ptr->places.size(), ImGui::GetTextLineHeightWithSpacing());
3212
                    while (group_ptr->clipper.Step()) {
3213
                        for (int i = group_ptr->clipper.DisplayStart; i < group_ptr->clipper.DisplayEnd; i++) {
3214
                            if (i < 0) {
3215
                                continue;
3216
                            }
3217
                            const PlaceStruct& place = group_ptr->places[(size_t)i];
3218
                            if (place.thickness > 0.0f) {
3219
                                ImGui::SeparatorEx(ImGuiSeparatorFlags_Horizontal, place.thickness);
3220
                            } else {
3221
                                ImGui::PushID(i);
3222
                                std::string place_name = place.name;
3223
                                if (!place.style.icon.empty()) {
3224
                                    place_name = place.style.icon + " " + place_name;
3225
                                }
3226
                                if (group_ptr->canBeEdited) {
3227
                                    ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
3228
                                    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
3229
                                    if (ImGui::SmallButton(editPlaceButtonString "##ImGuiFileDialogPlaceEditButton")) {
3230
                                        group_ptr->selectedPlaceForEdition = i;
3231
                                        IGFD::Utils::ResetBuffer(group_ptr->editBuffer);
3232
                                        IGFD::Utils::AppendToBuffer(group_ptr->editBuffer, MAX_FILE_DIALOG_NAME_BUFFER, place.name);
3233
                                    }
3234
                                    ImGui::PopStyleVar();
3235
                                    ImGui::PopStyleColor();
3236
                                    ImGui::SameLine();
3237
                                }
3238
                                if (ImGui::Selectable(place_name.c_str(), current_path == place.path || group_ptr->selectedPlaceForEdition == i, ImGuiSelectableFlags_AllowDoubleClick)) {  // select if path is current
3239
                                    if (ImGui::IsMouseDoubleClicked(0)) {
3240
                                        group_ptr->selectedPlaceForEdition = -1;  // stop edition
3241
                                        // apply path
3242
                                        vFileDialogInternal.fileManager.SetCurrentPath(place.path);
3243
                                        vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
3244
                                        res = true;
3245
                                    }
3246
                                }
3247
                                ImGui::PopID();
3248
                                if (ImGui::IsItemHovered()) {
3249
                                    ImGui::SetTooltip("%s", place.path.c_str());
3250
                                }
3251
                            }
3252
                        }
3253
                    }
3254
                    group_ptr->clipper.End();
3255
                }
3256
                ImGui::EndChild();
3257
            }
3258
        }
3259
    }
3260
    ImGui::EndChild();
3261
    return res;
3262
}
3263
3264
std::string IGFD::PlacesFeature::SerializePlaces(const bool /*vForceSerialisationForAll*/) {
3265
    std::string res;
3266
    size_t idx = 0;
3267
    for (const auto& group : m_Groups) {
3268
        if (group.second->canBeSaved) {
3269
            // ## is used because reserved by imgui, so an input text cannot have ##
3270
            res += "###" + group.first + "###";
3271
            for (const auto& place : group.second->places) {
3272
                if (place.canBeSaved) {
3273
                    if (idx++ != 0) res += "##";
3274
                    res += place.name + "##" + place.path;
3275
                }
3276
            }
3277
        }
3278
    }
3279
    return res;
3280
}
3281
3282
void IGFD::PlacesFeature::DeserializePlaces(const std::string& vPlaces) {
3283
    if (!vPlaces.empty()) {
3284
        const auto& groups = IGFD::Utils::SplitStringToVector(vPlaces, "###", false);
3285
        if (groups.size() > 1) {
3286
            for (size_t i = 0; i < groups.size(); i += 2) {
3287
                auto group_ptr = GetPlacesGroupPtr(groups[i]);
3288
                if (group_ptr != nullptr) {
3289
                    const auto& places = IGFD::Utils::SplitStringToVector(groups[i + 1], "##", false);
3290
                    if (places.size() > 1) {
3291
                        for (size_t j = 0; j < places.size(); j += 2) {
3292
                            group_ptr->AddPlace(places[j], places[j + 1], true);  // was saved so we set canBeSaved to true
3293
                        }
3294
                    }
3295
                }
3296
            }
3297
        }
3298
    }
3299
}
3300
3301
bool IGFD::PlacesFeature::AddPlacesGroup(const std::string& vGroupName, const size_t& vDisplayOrder, const bool vCanBeEdited, const bool vOpenedByDefault) {
3302
    if (vGroupName.empty()) {
3303
        return false;
3304
    }
3305
    auto group_ptr           = std::make_shared<GroupStruct>();
3306
    group_ptr->displayOrder  = vDisplayOrder;
3307
    group_ptr->name          = vGroupName;
3308
    group_ptr->defaultOpened = vOpenedByDefault;
3309
    if (group_ptr->defaultOpened) {
3310
        group_ptr->collapsingHeaderFlag = ImGuiTreeNodeFlags_DefaultOpen;
3311
    }
3312
    group_ptr->canBeSaved = group_ptr->canBeEdited = vCanBeEdited;  // can be user edited mean can be saved
3313
    m_Groups[vGroupName]                           = group_ptr;
3314
    m_OrderedGroups[group_ptr->displayOrder]       = group_ptr;  // an exisitng display order will be overwrote for code simplicity
3315
    return true;
3316
}
3317
3318
bool IGFD::PlacesFeature::RemovePlacesGroup(const std::string& vGroupName) {
3319
    for (auto it = m_Groups.begin(); it != m_Groups.end(); ++it) {
3320
        if ((*it).second->name == vGroupName) {
3321
            m_Groups.erase(it);
3322
            return true;
3323
        }
3324
    }
3325
    return false;
3326
}
3327
3328
IGFD::PlacesFeature::GroupStruct* IGFD::PlacesFeature::GetPlacesGroupPtr(const std::string& vGroupName) {
3329
    if (m_Groups.find(vGroupName) != m_Groups.end()) {
3330
        return m_Groups.at(vGroupName).get();
3331
    }
3332
    return nullptr;
3333
}
3334
3335
bool IGFD::PlacesFeature::GroupStruct::AddPlace(const std::string& vPlaceName, const std::string& vPlacePath, const bool vCanBeSaved, const FileStyle& vStyle) {
3336
    if (vPlaceName.empty() || vPlacePath.empty()) {
3337
        return false;
3338
    }
3339
    canBeSaved |= vCanBeSaved;  // if one place must be saved so we mark the group to be saved
3340
    PlaceStruct place;
3341
    place.name       = vPlaceName;
3342
    place.path       = vPlacePath;
3343
    place.canBeSaved = vCanBeSaved;
3344
    place.style      = vStyle;
3345
    places.push_back(place);
3346
    return true;
3347
}
3348
3349
void IGFD::PlacesFeature::GroupStruct::AddPlaceSeparator(const float& vThickness) {
3350
    PlaceStruct place;
3351
    place.thickness = vThickness;
3352
    places.push_back(place);
3353
}
3354
3355
bool IGFD::PlacesFeature::GroupStruct::RemovePlace(const std::string& vPlaceName) {
3356
    if (vPlaceName.empty()) {
3357
        return false;
3358
    }
3359
    for (auto places_it = places.begin(); places_it != places.end(); ++places_it) {
3360
        if ((*places_it).name == vPlaceName) {
3361
            places.erase(places_it);
3362
            return true;
3363
        }
3364
    }
3365
    return false;
3366
}
3367
#endif  // USE_PLACES_FEATURE
3368
3369
1
IGFD::KeyExplorerFeature::KeyExplorerFeature() = default;
3370
3371
#ifdef USE_EXPLORATION_BY_KEYS
3372
bool IGFD::KeyExplorerFeature::m_LocateItem_Loop(FileDialogInternal& vFileDialogInternal, ImWchar vC) {
3373
    bool found = false;
3374
3375
    auto& fdi = vFileDialogInternal.fileManager;
3376
    if (!fdi.IsFilteredListEmpty()) {
3377
        auto countFiles = fdi.GetFilteredListSize();
3378
        for (size_t i = m_LocateFileByInputChar_lastFileIdx; i < countFiles; i++) {
3379
            auto nfo = fdi.GetFilteredFileAt(i);
3380
            if (nfo.use_count()) {
3381
                if (nfo->fileNameExt_optimized[0] == vC ||  // lower case search //-V522
3382
                    nfo->fileNameExt[0] == vC)              // maybe upper case search
3383
                {
3384
                    // float p = ((float)i) * ImGui::GetTextLineHeightWithSpacing();
3385
                    float p = (float)((double)i / (double)countFiles) * ImGui::GetScrollMaxY();
3386
                    ImGui::SetScrollY(p);
3387
                    m_LocateFileByInputChar_lastFound   = true;
3388
                    m_LocateFileByInputChar_lastFileIdx = i;
3389
                    m_StartFlashItem(m_LocateFileByInputChar_lastFileIdx);
3390
3391
                    auto pInfos = fdi.GetFilteredFileAt(m_LocateFileByInputChar_lastFileIdx);
3392
                    if (pInfos.use_count()) {
3393
                        if (pInfos->fileType.isDir())  //-V522
3394
                        {
3395
                            if (fdi.dLGDirectoryMode)  // directory chooser
3396
                            {
3397
                                fdi.SelectFileName(pInfos);
3398
                            }
3399
                        } else {
3400
                            fdi.SelectFileName(pInfos);
3401
                        }
3402
3403
                        found = true;
3404
                        break;
3405
                    }
3406
                }
3407
            }
3408
        }
3409
    }
3410
3411
    return found;
3412
}
3413
3414
void IGFD::KeyExplorerFeature::m_LocateByInputKey(FileDialogInternal& vFileDialogInternal) {
3415
    ImGuiContext& g = *GImGui;
3416
    auto& fdi       = vFileDialogInternal.fileManager;
3417
    if (!g.ActiveId && !fdi.IsFilteredListEmpty()) {
3418
        auto& queueChar = ImGui::GetIO().InputQueueCharacters;
3419
        auto countFiles = fdi.GetFilteredListSize();
3420
3421
        // point by char
3422
        if (!queueChar.empty()) {
3423
            ImWchar c = queueChar.back();
3424
            if (m_LocateFileByInputChar_InputQueueCharactersSize != queueChar.size()) {
3425
                if (c == m_LocateFileByInputChar_lastChar)  // next file starting with same char until
3426
                {
3427
                    if (m_LocateFileByInputChar_lastFileIdx < countFiles - 1U)
3428
                        m_LocateFileByInputChar_lastFileIdx++;
3429
                    else
3430
                        m_LocateFileByInputChar_lastFileIdx = 0;
3431
                }
3432
3433
                if (!m_LocateItem_Loop(vFileDialogInternal, c)) {
3434
                    // not found, loop again from 0 this time
3435
                    m_LocateFileByInputChar_lastFileIdx = 0;
3436
                    m_LocateItem_Loop(vFileDialogInternal, c);
3437
                }
3438
3439
                m_LocateFileByInputChar_lastChar = c;
3440
            }
3441
        }
3442
3443
        m_LocateFileByInputChar_InputQueueCharactersSize = queueChar.size();
3444
    }
3445
}
3446
3447
void IGFD::KeyExplorerFeature::m_ExploreWithkeys(FileDialogInternal& vFileDialogInternal, ImGuiID vListViewID) {
3448
    auto& fdi = vFileDialogInternal.fileManager;
3449
    if (!fdi.IsFilteredListEmpty()) {
3450
        bool canWeExplore = false;
3451
        bool hasNav       = (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard);
3452
3453
        ImGuiContext& g = *GImGui;
3454
        if (!hasNav && !g.ActiveId)  // no nav and no activated inputs
3455
            canWeExplore = true;
3456
3457
        if (g.NavId && g.NavId == vListViewID) {
3458
            if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter) || ImGui::IsKeyPressed(ImGuiKey_Space)) {
3459
                ImGui::ActivateItemByID(vListViewID);
3460
                ImGui::SetActiveID(vListViewID, g.CurrentWindow);
3461
            }
3462
        }
3463
3464
        if (vListViewID == g.LastActiveId - 1)  // if listview id is the last acticated nav id (ImGui::ActivateItemByID(vListViewID);)
3465
            canWeExplore = true;
3466
3467
        if (canWeExplore && ImGui::IsWindowFocused()) {
3468
            if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
3469
                ImGui::ClearActiveID();
3470
                g.LastActiveId = 0;
3471
            }
3472
3473
            auto countFiles = fdi.GetFilteredListSize();
3474
3475
            // explore
3476
            bool exploreByKey     = false;
3477
            bool enterInDirectory = false;
3478
            bool exitDirectory    = false;
3479
3480
            if ((hasNav && ImGui::IsKeyPressed(ImGuiKey_UpArrow)) || (!hasNav && ImGui::IsKeyPressed(ImGuiKey_UpArrow))) {
3481
                exploreByKey = true;
3482
                if (m_LocateFileByInputChar_lastFileIdx > 0)
3483
                    m_LocateFileByInputChar_lastFileIdx--;
3484
                else
3485
                    m_LocateFileByInputChar_lastFileIdx = countFiles - 1U;
3486
            } else if ((hasNav && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) || (!hasNav && ImGui::IsKeyPressed(ImGuiKey_DownArrow))) {
3487
                exploreByKey = true;
3488
                if (m_LocateFileByInputChar_lastFileIdx < countFiles - 1U)
3489
                    m_LocateFileByInputChar_lastFileIdx++;
3490
                else
3491
                    m_LocateFileByInputChar_lastFileIdx = 0U;
3492
            } else if (ImGui::IsKeyReleased(ImGuiKey_Enter)) {
3493
                exploreByKey     = true;
3494
                enterInDirectory = true;
3495
            } else if (ImGui::IsKeyReleased(ImGuiKey_Backspace)) {
3496
                exploreByKey  = true;
3497
                exitDirectory = true;
3498
            }
3499
3500
            if (exploreByKey) {
3501
                // float totalHeight = m_FilteredFileList.size() * ImGui::GetTextLineHeightWithSpacing();
3502
                float p = (float)((double)m_LocateFileByInputChar_lastFileIdx / (double)(countFiles - 1U)) * ImGui::GetScrollMaxY();  // seems not udpated in tables version outside tables
3503
                // float p = ((float)locateFileByInputChar_lastFileIdx) * ImGui::GetTextLineHeightWithSpacing();
3504
                ImGui::SetScrollY(p);
3505
                m_StartFlashItem(m_LocateFileByInputChar_lastFileIdx);
3506
3507
                auto pInfos = fdi.GetFilteredFileAt(m_LocateFileByInputChar_lastFileIdx);
3508
                if (pInfos.use_count()) {
3509
                    if (pInfos->fileType.isDir())  //-V522
3510
                    {
3511
                        if (!fdi.dLGDirectoryMode || enterInDirectory) {
3512
                            if (enterInDirectory) {
3513
                                if (fdi.SelectDirectory(pInfos)) {
3514
                                    // changement de repertoire
3515
                                    vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
3516
                                    if (m_LocateFileByInputChar_lastFileIdx > countFiles - 1U) {
3517
                                        m_LocateFileByInputChar_lastFileIdx = 0;
3518
                                    }
3519
                                }
3520
                            }
3521
                        } else  // directory chooser
3522
                        {
3523
                            fdi.SelectFileName(pInfos);
3524
                        }
3525
                    } else {
3526
                        fdi.SelectFileName(pInfos);
3527
3528
                        if (enterInDirectory) {
3529
                            vFileDialogInternal.isOk = true;
3530
                        }
3531
                    }
3532
3533
                    if (exitDirectory) {
3534
                        auto nfo_ptr         = FileInfos::create();
3535
                        nfo_ptr->fileNameExt = "..";
3536
3537
                        if (fdi.SelectDirectory(nfo_ptr)) {
3538
                            // changement de repertoire
3539
                            vFileDialogInternal.fileManager.OpenCurrentPath(vFileDialogInternal);
3540
                            if (m_LocateFileByInputChar_lastFileIdx > countFiles - 1U) {
3541
                                m_LocateFileByInputChar_lastFileIdx = 0;
3542
                            }
3543
                        }
3544
#ifdef _IGFD_WIN_
3545
                        else {
3546
                            if (fdi.GetComposerSize() == 1U) {
3547
                                if (fdi.GetDevices()) {
3548
                                    fdi.ApplyFilteringOnFileList(vFileDialogInternal);
3549
                                }
3550
                            }
3551
                        }
3552
#endif  // _IGFD_WIN_
3553
                    }
3554
                }
3555
            }
3556
        }
3557
    }
3558
}
3559
3560
bool IGFD::KeyExplorerFeature::m_FlashableSelectable(const char* label, bool selected, ImGuiSelectableFlags flags, bool vFlashing, const ImVec2& size_arg) {
3561
    using namespace ImGui;
3562
3563
    ImGuiWindow* window = GetCurrentWindow();
3564
    if (window->SkipItems) return false;
3565
3566
    ImGuiContext& g         = *GImGui;
3567
    const ImGuiStyle& style = g.Style;
3568
3569
    // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
3570
    ImGuiID id        = window->GetID(label);
3571
    ImVec2 label_size = CalcTextSize(label, NULL, true);
3572
    ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
3573
    ImVec2 pos = window->DC.CursorPos;
3574
    pos.y += window->DC.CurrLineTextBaseOffset;
3575
    ItemSize(size, 0.0f);
3576
3577
    // Fill horizontal space
3578
    // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.
3579
    const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;
3580
    const float min_x           = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;
3581
    const float max_x           = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
3582
    if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x);
3583
3584
    // Text stays at the submission position, but bounding box may be extended on both sides
3585
    const ImVec2 text_min = pos;
3586
    const ImVec2 text_max(min_x + size.x, pos.y + size.y);
3587
3588
    // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
3589
    // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currenty doesn't allow offsetting CursorPos.
3590
    ImRect bb(min_x, pos.y, text_max.x, text_max.y);
3591
    if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) {
3592
        const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
3593
        const float spacing_y = style.ItemSpacing.y;
3594
        const float spacing_L = IM_TRUNC(spacing_x * 0.50f);
3595
        const float spacing_U = IM_TRUNC(spacing_y * 0.50f);
3596
        bb.Min.x -= spacing_L;
3597
        bb.Min.y -= spacing_U;
3598
        bb.Max.x += (spacing_x - spacing_L);
3599
        bb.Max.y += (spacing_y - spacing_U);
3600
    }
3601
    // if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
3602
3603
    // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
3604
    const float backup_clip_rect_min_x = window->ClipRect.Min.x;
3605
    const float backup_clip_rect_max_x = window->ClipRect.Max.x;
3606
    if (span_all_columns) {
3607
        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
3608
        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
3609
    }
3610
3611
    const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
3612
    const bool is_visible    = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None);
3613
3614
    if (span_all_columns) {
3615
        window->ClipRect.Min.x = backup_clip_rect_min_x;
3616
        window->ClipRect.Max.x = backup_clip_rect_max_x;
3617
    }
3618
3619
    const bool is_multi_select = (g.LastItemData.ItemFlags & ImGuiItemFlags_IsMultiSelect) != 0;
3620
    if (!is_visible)
3621
        if (!is_multi_select || !g.BoxSelectState.UnclipMode || !g.BoxSelectState.UnclipRect.Overlaps(bb))  // Extra layer of "no logic clip" for box-select support (would be more overhead to add to ItemAdd)
3622
            return false;
3623
3624
    const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
3625
    if (disabled_item && !disabled_global)  // Only testing this as an optimization
3626
        BeginDisabled();
3627
3628
    // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,
3629
    // which would be advantageous since most selectable are not selected.
3630
    if (span_all_columns) {
3631
        if (g.CurrentTable)
3632
            TablePushBackgroundChannel();
3633
        else if (window->DC.CurrentColumns)
3634
            PushColumnsBackground();
3635
        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect;
3636
        g.LastItemData.ClipRect = window->ClipRect;
3637
    }
3638
3639
    // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
3640
    ImGuiButtonFlags button_flags = 0;
3641
    if (flags & ImGuiSelectableFlags_NoHoldingActiveID) {
3642
        button_flags |= ImGuiButtonFlags_NoHoldingActiveId;
3643
    }
3644
    if (flags & ImGuiSelectableFlags_NoSetKeyOwner) {
3645
        button_flags |= ImGuiButtonFlags_NoSetKeyOwner;
3646
    }
3647
    if (flags & ImGuiSelectableFlags_SelectOnClick) {
3648
        button_flags |= ImGuiButtonFlags_PressedOnClick;
3649
    }
3650
    if (flags & ImGuiSelectableFlags_SelectOnRelease) {
3651
        button_flags |= ImGuiButtonFlags_PressedOnRelease;
3652
    }
3653
    if (flags & ImGuiSelectableFlags_AllowDoubleClick) {
3654
        button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
3655
    }
3656
    if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.ItemFlags & ImGuiItemFlags_AllowOverlap)) {
3657
        button_flags |= ImGuiButtonFlags_AllowOverlap;
3658
    }
3659
3660
    // Multi-selection support (header)
3661
    const bool was_selected = selected;
3662
    if (is_multi_select) {
3663
        // Handle multi-select + alter button flags for it
3664
        MultiSelectItemHeader(id, &selected, &button_flags);
3665
    }
3666
3667
    bool hovered, held;
3668
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
3669
3670
    // Multi-selection support (footer)
3671
    if (is_multi_select) {
3672
        MultiSelectItemFooter(id, &selected, &pressed);
3673
    } else {
3674
        // Auto-select when moved into
3675
        // - This will be more fully fleshed in the range-select branch
3676
        // - This is not exposed as it won't nicely work with some user side handling of shift/control
3677
        // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons
3678
        //   - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())
3679
        //   - (2) usage will fail with clipped items
3680
        //   The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.
3681
        if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)
3682
            if (g.NavJustMovedToId == id) selected = pressed = true;
3683
    }
3684
3685
    //////////////////////////////////////////////////////////////////
3686
    // this function copy ImGui::Selectable just for this line....
3687
    hovered |= vFlashing;
3688
    //////////////////////////////////////////////////////////////////
3689
3690
    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
3691
    if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover))) {
3692
        if (!g.NavHighlightItemUnderNav && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) {
3693
            SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb));  // (bb == NavRect)
3694
            g.NavCursorVisible = false;
3695
        }
3696
    }
3697
    if (pressed) MarkItemEdited(id);
3698
3699
    if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
3700
3701
    // Render
3702
    if (is_visible) {
3703
        if (hovered || selected) {
3704
            // FIXME-MULTISELECT: Styling: Color for 'selected' elements? ImGuiCol_HeaderSelected
3705
            ImU32 col;
3706
            if (selected && !hovered)
3707
                col = GetColorU32(ImLerp(GetStyleColorVec4(ImGuiCol_Header), GetStyleColorVec4(ImGuiCol_HeaderHovered), 0.5f));
3708
            else
3709
                col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
3710
            RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
3711
        }
3712
        if (g.NavId == id) {
3713
            ImGuiNavRenderCursorFlags flags = ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding;
3714
            if (is_multi_select) flags |= ImGuiNavRenderCursorFlags_AlwaysDraw;  // Always show the nav rectangle
3715
            RenderNavCursor(bb, id, flags);
3716
        }
3717
    }
3718
3719
    if (span_all_columns) {
3720
        if (g.CurrentTable)
3721
            TablePopBackgroundChannel();
3722
        else if (window->DC.CurrentColumns)
3723
            PopColumnsBackground();
3724
    }
3725
3726
    if (is_visible) RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
3727
3728
    // Automatically close popups
3729
    if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) CloseCurrentPopup();
3730
3731
    if (disabled_item && !disabled_global) EndDisabled();
3732
3733
    // Selectable() always returns a pressed state!
3734
    // Users of BeginMultiSelect()/EndMultiSelect() scope: you may call ImGui::IsItemToggledSelection() to retrieve
3735
    // selection toggle, only useful if you need that state updated (e.g. for rendering purpose) before reaching EndMultiSelect().
3736
    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
3737
    return pressed;  //-V1020
3738
}
3739
3740
void IGFD::KeyExplorerFeature::m_StartFlashItem(size_t vIdx) {
3741
    m_FlashAlpha  = 1.0f;
3742
    m_FlashedItem = vIdx;
3743
}
3744
3745
bool IGFD::KeyExplorerFeature::m_BeginFlashItem(size_t vIdx) {
3746
    bool res = false;
3747
3748
    if (m_FlashedItem == vIdx && std::abs(m_FlashAlpha - 0.0f) > 0.00001f) {
3749
        m_FlashAlpha -= m_FlashAlphaAttenInSecs * ImGui::GetIO().DeltaTime;
3750
        if (m_FlashAlpha < 0.0f) m_FlashAlpha = 0.0f;
3751
3752
        ImVec4 hov = ImGui::GetStyleColorVec4(ImGuiCol_HeaderHovered);
3753
        hov.w      = m_FlashAlpha;
3754
        ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hov);
3755
        res = true;
3756
    }
3757
3758
    return res;
3759
}
3760
3761
void IGFD::KeyExplorerFeature::m_EndFlashItem() {
3762
    ImGui::PopStyleColor();
3763
}
3764
3765
void IGFD::KeyExplorerFeature::SetFlashingAttenuationInSeconds(float vAttenValue) {
3766
    m_FlashAlphaAttenInSecs = 1.0f / ImMax(vAttenValue, 0.01f);
3767
}
3768
#endif  // USE_EXPLORATION_BY_KEYS
3769
3770
1
IGFD::FileDialog::FileDialog() : PlacesFeature(), KeyExplorerFeature(), ThumbnailFeature() {
3771
1
}
3772
1
IGFD::FileDialog::~FileDialog() = default;
3773
3774
//////////////////////////////////////////////////////////////////////////////////////////////////
3775
///// FILE DIALOG STANDARD DIALOG ////////////////////////////////////////////////////////////////
3776
//////////////////////////////////////////////////////////////////////////////////////////////////
3777
3778
// path and fileNameExt can be specified
3779
0
void IGFD::FileDialog::OpenDialog(const std::string& vKey, const std::string& vTitle, const char* vFilters, const FileDialogConfig& vConfig) {
3780
0
    if (m_FileDialogInternal.showDialog)  // if already opened, quit
3781
0
        return;
3782
0
    m_FileDialogInternal.configureDialog(vKey, vTitle, vFilters, vConfig);
3783
#ifdef USE_PLACES_FEATURE
3784
    m_InitPlaces(m_FileDialogInternal);
3785
#endif
3786
0
}
3787
3788
//////////////////////////////////////////////////////////////////////////////////////////////////
3789
///// FILE DIALOG DISPLAY FUNCTION ///////////////////////////////////////////////////////////////
3790
//////////////////////////////////////////////////////////////////////////////////////////////////
3791
3792
161k
bool IGFD::FileDialog::Display(const std::string& vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) {
3793
161k
    bool res = false;
3794
3795
161k
    if (m_FileDialogInternal.showDialog && m_FileDialogInternal.dLGkey == vKey) {
3796
0
        if (m_FileDialogInternal.puUseCustomLocale) setlocale(m_FileDialogInternal.localeCategory, m_FileDialogInternal.localeBegin.c_str());
3797
3798
0
        auto& fdFile   = m_FileDialogInternal.fileManager;
3799
0
        auto& fdFilter = m_FileDialogInternal.filterManager;
3800
3801
        // to be sure than only one dialog is displayed per frame
3802
0
        ImGuiContext& g = *GImGui;
3803
0
        if (g.FrameCount == m_FileDialogInternal.lastImGuiFrameCount) {  // one instance was displayed this frame before
3804
0
            return res;                                                  // for this key +> quit
3805
0
        }
3806
0
        m_FileDialogInternal.lastImGuiFrameCount = g.FrameCount;  // mark this instance as used this frame
3807
3808
0
        m_CurrentDisplayedFlags = vFlags;
3809
0
        std::string name        = m_FileDialogInternal.dLGtitle + "##" + m_FileDialogInternal.dLGkey;
3810
0
        if (m_FileDialogInternal.name != name) {
3811
0
            fdFile.ClearComposer();
3812
0
            fdFile.ClearFileLists();
3813
0
        }
3814
3815
0
        m_NewFrame();
3816
3817
#ifdef IMGUI_HAS_VIEWPORT
3818
        if (!ImGui::GetIO().ConfigViewportsNoDecoration) {
3819
            // https://github.com/ocornut/imgui/issues/4534
3820
            ImGuiWindowClass window_class;
3821
            window_class.ViewportFlagsOverrideClear = ImGuiViewportFlags_NoDecoration;
3822
            ImGui::SetNextWindowClass(&window_class);
3823
        }
3824
#endif  // IMGUI_HAS_VIEWPORT
3825
3826
0
        bool beg         = false;
3827
0
        ImVec2 frameSize = ImVec2(0, 0);
3828
0
        if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_NoDialog) {  // disable our own dialog system (standard or modal)
3829
0
            frameSize = vMinSize;
3830
0
            beg       = true;
3831
0
        } else {
3832
0
            ImGui::SetNextWindowSizeConstraints(vMinSize, vMaxSize);
3833
0
            if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_Modal &&  // disable modal because the confirm dialog for overwrite is
3834
0
                !m_FileDialogInternal.okResultToConfirm) {                                    // a new modal
3835
0
                ImGui::OpenPopup(name.c_str());
3836
0
                beg = ImGui::BeginPopupModal(name.c_str(), (bool*)nullptr, m_CurrentDisplayedFlags | ImGuiWindowFlags_NoScrollbar);
3837
0
            } else {
3838
0
                beg = ImGui::Begin(name.c_str(), (bool*)nullptr, m_CurrentDisplayedFlags | ImGuiWindowFlags_NoScrollbar);
3839
0
            }
3840
0
        }
3841
0
        if (beg) {
3842
#ifdef IMGUI_HAS_VIEWPORT
3843
            // if decoration is enabled we disable the resizing feature of imgui for avoid crash with SDL2 and GLFW3
3844
            if (ImGui::GetIO().ConfigViewportsNoDecoration) {
3845
                m_CurrentDisplayedFlags = vFlags;
3846
            } else {
3847
                auto win = ImGui::GetCurrentWindowRead();
3848
                if (win->Viewport->Idx != 0)
3849
                    m_CurrentDisplayedFlags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar;
3850
                else
3851
                    m_CurrentDisplayedFlags = vFlags;
3852
            }
3853
#endif  // IMGUI_HAS_VIEWPORT
3854
3855
0
            if (ImGui::BeginChild("childContent", frameSize, false, m_CurrentDisplayedFlags | ImGuiWindowFlags_NoScrollbar)) {
3856
0
                m_FileDialogInternal.name = name;  //-V820
3857
0
                if (fdFile.dLGpath.empty()) {
3858
0
                    fdFile.dLGpath = ".";  // defaut path is '.'
3859
0
                }
3860
0
                fdFilter.SetDefaultFilterIfNotDefined();
3861
3862
                // init list of files
3863
0
                if (fdFile.IsFileListEmpty() && !fdFile.showDevices) {
3864
0
                    if (fdFile.dLGpath != ".")                                                      // Removes extension seperator in filename if we don't check
3865
0
                        IGFD::Utils::ReplaceString(fdFile.dLGDefaultFileName, fdFile.dLGpath, "");  // local path
3866
3867
0
                    if (!fdFile.dLGDefaultFileName.empty()) {
3868
0
                        fdFile.SetDefaultFileName(fdFile.dLGDefaultFileName);
3869
0
                        fdFilter.SetSelectedFilterWithExt(fdFilter.dLGdefaultExt);
3870
0
                    } else if (fdFile.dLGDirectoryMode)  // directory mode
3871
0
                        fdFile.SetDefaultFileName(".");
3872
0
                    fdFile.ScanDir(m_FileDialogInternal, fdFile.dLGpath);
3873
0
                }
3874
3875
                // draw dialog parts
3876
0
                m_DrawHeader();        // place, directory, path
3877
0
                m_DrawContent();       // place, files view, side pane
3878
0
                res = m_DrawFooter();  // file field, filter combobox, ok/cancel buttons
3879
3880
0
                m_EndFrame();
3881
0
            }
3882
0
            ImGui::EndChild();
3883
3884
            // for display in dialog center, the confirm to overwrite dlg
3885
0
            m_FileDialogInternal.dialogCenterPos = ImGui::GetCurrentWindowRead()->ContentRegionRect.GetCenter();
3886
3887
            // when the confirm to overwrite dialog will appear we need to
3888
            // disable the modal mode of the main file dialog
3889
            // see prOkResultToConfirm under
3890
0
            if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_Modal && !m_FileDialogInternal.okResultToConfirm) {
3891
0
                ImGui::EndPopup();
3892
0
            }
3893
0
        }
3894
3895
0
        if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_NoDialog) {  // disable our own dialog system (standard or modal)
3896
0
        } else {
3897
            // same things here regarding prOkResultToConfirm
3898
0
            if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_Modal) || m_FileDialogInternal.okResultToConfirm) {
3899
0
                ImGui::End();
3900
0
            }
3901
0
        }
3902
        // confirm the result and show the confirm to overwrite dialog if needed
3903
0
        res = m_Confirm_Or_OpenOverWriteFileDialog_IfNeeded(res, vFlags);
3904
3905
0
        if (m_FileDialogInternal.puUseCustomLocale) setlocale(m_FileDialogInternal.localeCategory, m_FileDialogInternal.localeEnd.c_str());
3906
0
    }
3907
3908
161k
    return res;
3909
161k
}
3910
3911
0
void IGFD::FileDialog::m_NewFrame() {
3912
0
    m_FileDialogInternal.NewFrame();
3913
0
    m_NewThumbnailFrame(m_FileDialogInternal);
3914
0
}
3915
3916
0
void IGFD::FileDialog::m_EndFrame() {
3917
0
    m_EndThumbnailFrame(m_FileDialogInternal);
3918
0
    m_FileDialogInternal.EndFrame();
3919
0
}
3920
0
void IGFD::FileDialog::m_QuitFrame() {
3921
0
    m_QuitThumbnailFrame(m_FileDialogInternal);
3922
0
}
3923
3924
0
void IGFD::FileDialog::m_DrawHeader() {
3925
#ifdef USE_PLACES_FEATURE
3926
    if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisablePlaceMode)) {
3927
        m_DrawPlacesButton();
3928
        ImGui::SameLine();
3929
    }
3930
3931
#endif  // USE_PLACES_FEATURE
3932
3933
0
    m_FileDialogInternal.fileManager.DrawDirectoryCreation(m_FileDialogInternal);
3934
3935
0
    if (
3936
#ifdef USE_PLACES_FEATURE
3937
        !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisablePlaceMode) ||
3938
#endif  // USE_PLACES_FEATURE
3939
0
        !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableCreateDirectoryButton)) {
3940
0
        ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
3941
0
        ImGui::SameLine();
3942
0
    }
3943
0
    m_FileDialogInternal.fileManager.DrawPathComposer(m_FileDialogInternal);
3944
3945
#ifdef USE_THUMBNAILS
3946
    if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableThumbnailMode)) {
3947
        m_DrawDisplayModeToolBar();
3948
        ImGui::SameLine();
3949
        ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
3950
        ImGui::SameLine();
3951
    }
3952
#endif  // USE_THUMBNAILS
3953
3954
0
    m_FileDialogInternal.searchManager.DrawSearchBar(m_FileDialogInternal);
3955
0
}
3956
3957
0
void IGFD::FileDialog::m_DrawContent() {
3958
0
    ImVec2 size = ImGui::GetContentRegionAvail() - ImVec2(0.0f, m_FileDialogInternal.footerHeight);
3959
3960
#ifdef USE_PLACES_FEATURE
3961
    if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisablePlaceMode)) {
3962
        if (m_PlacesPaneShown) {
3963
            float otherWidth = size.x - m_PlacesPaneWidth;
3964
            ImGui::PushID("splitterplaces");
3965
            IGFD::Utils::ImSplitter(true, 4.0f, &m_PlacesPaneWidth, &otherWidth, 10.0f, 10.0f + m_FileDialogInternal.getDialogConfig().sidePaneWidth, size.y);
3966
            ImGui::PopID();
3967
            size.x -= otherWidth;
3968
            m_DrawPlacesPane(m_FileDialogInternal, size);
3969
            ImGui::SameLine();
3970
        }
3971
    }
3972
#endif  // USE_PLACES_FEATURE
3973
3974
0
    size.x = ImGui::GetContentRegionAvail().x - m_FileDialogInternal.getDialogConfig().sidePaneWidth;
3975
3976
0
    if (m_FileDialogInternal.getDialogConfig().sidePane) {
3977
0
        ImGui::PushID("splittersidepane");
3978
0
        IGFD::Utils::ImSplitter(true, 4.0f, &size.x, &m_FileDialogInternal.getDialogConfigRef().sidePaneWidth, 10.0f, 10.0f, size.y);
3979
0
        ImGui::PopID();
3980
0
    }
3981
3982
#ifdef USE_THUMBNAILS
3983
    if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableThumbnailMode) {
3984
        m_DrawFileListView(size);
3985
    } else {
3986
        switch (m_DisplayMode) {
3987
            case DisplayModeEnum::FILE_LIST: m_DrawFileListView(size); break;
3988
            case DisplayModeEnum::THUMBNAILS_LIST: m_DrawThumbnailsListView(size); break;
3989
            case DisplayModeEnum::THUMBNAILS_GRID: m_DrawThumbnailsGridView(size);
3990
        }
3991
    }
3992
#else   // USE_THUMBNAILS
3993
0
    m_DrawFileListView(size);
3994
0
#endif  // USE_THUMBNAILS
3995
3996
0
    if (m_FileDialogInternal.getDialogConfig().sidePane) {
3997
0
        m_DrawSidePane(size.y);
3998
0
    }
3999
4000
0
    if (!(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableQuickPathSelection)) {
4001
0
        m_DisplayPathPopup(size);
4002
0
    }
4003
0
}
4004
4005
0
void IGFD::FileDialog::m_DisplayPathPopup(ImVec2 vSize) {
4006
0
    ImVec2 size = ImVec2(vSize.x * 0.5f, vSize.y * 0.5f);
4007
0
    if (ImGui::BeginPopup("IGFD_Path_Popup")) {
4008
0
        auto& fdi = m_FileDialogInternal.fileManager;
4009
4010
0
        ImGui::PushID(this);
4011
4012
0
        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_NoHostExtendY;
4013
0
        auto listViewID              = ImGui::GetID("##FileDialog_pathTable");
4014
0
        if (ImGui::BeginTableEx("##FileDialog_pathTable", listViewID, 1, flags, size, 0.0f))  //-V112
4015
0
        {
4016
0
            ImGui::TableSetupScrollFreeze(0, 1);  // Make header always visible
4017
0
            ImGui::TableSetupColumn(tableHeaderFileNameString, ImGuiTableColumnFlags_WidthStretch | (defaultSortOrderFilename ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 0);
4018
4019
0
            ImGui::TableHeadersRow();
4020
4021
0
            if (!fdi.IsPathFilteredListEmpty()) {
4022
0
                std::string _str;
4023
0
                ImFont* _font   = nullptr;
4024
0
                bool _showColor = false;
4025
4026
0
                m_PathListClipper.Begin((int)fdi.GetPathFilteredListSize(), ImGui::GetTextLineHeightWithSpacing());
4027
0
                while (m_PathListClipper.Step()) {
4028
0
                    for (int i = m_PathListClipper.DisplayStart; i < m_PathListClipper.DisplayEnd; i++) {
4029
0
                        if (i < 0) continue;
4030
4031
0
                        auto pInfos = fdi.GetFilteredPathAt((size_t)i);
4032
0
                        if (!pInfos.use_count()) continue;
4033
4034
0
                        m_BeginFileColorIconStyle(pInfos, _showColor, _str, &_font);
4035
4036
0
                        bool selected = fdi.IsFileNameSelected(pInfos->fileNameExt);  // found
4037
4038
0
                        ImGui::TableNextRow();
4039
4040
0
                        if (ImGui::TableNextColumn())  // file name
4041
0
                        {
4042
0
                            if (ImGui::Selectable(pInfos->fileNameExt.c_str(), &selected, static_cast<int>(ImGuiSelectableFlags_SpanAllColumns) | static_cast<int>(ImGuiSelectableFlags_SpanAvailWidth))) {
4043
0
                                fdi.SetCurrentPath(fdi.ComposeNewPath(fdi.GetCurrentPopupComposedPath()));
4044
0
                                fdi.pathClicked = fdi.SelectDirectory(pInfos);
4045
0
                                ImGui::CloseCurrentPopup();
4046
0
                            }
4047
0
                        }
4048
4049
0
                        m_EndFileColorIconStyle(_showColor, _font);
4050
0
                    }
4051
0
                }
4052
0
                m_PathListClipper.End();
4053
0
            }
4054
4055
0
            ImGui::EndTable();
4056
0
        }
4057
4058
0
        ImGui::PopID();
4059
4060
0
        ImGui::EndPopup();
4061
0
    }
4062
0
}
4063
4064
0
bool IGFD::FileDialog::m_DrawOkButton() {
4065
0
    auto& fdFile = m_FileDialogInternal.fileManager;
4066
0
    if ((m_FileDialogInternal.canWeContinue && strlen(fdFile.fileNameBuffer)) || //
4067
0
        (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_OptionalFileName)) { // optional
4068
0
        if (IMGUI_BUTTON(okButtonString "##validationdialog", ImVec2(okButtonWidth, 0.0f)) || m_FileDialogInternal.isOk) {
4069
0
            m_FileDialogInternal.isOk = true;
4070
0
            return true;
4071
0
        }
4072
4073
0
#if !invertOkAndCancelButtons
4074
0
        ImGui::SameLine();
4075
0
#endif
4076
0
    }
4077
4078
0
    return false;
4079
0
}
4080
4081
0
bool IGFD::FileDialog::m_DrawCancelButton() {
4082
0
    if (IMGUI_BUTTON(cancelButtonString "##validationdialog", ImVec2(cancelButtonWidth, 0.0f)) || m_FileDialogInternal.needToExitDialog)  // dialog exit asked
4083
0
    {
4084
0
        m_FileDialogInternal.isOk = false;
4085
0
        return true;
4086
0
    }
4087
4088
#if invertOkAndCancelButtons
4089
    ImGui::SameLine();
4090
#endif
4091
4092
0
    return false;
4093
0
}
4094
4095
0
bool IGFD::FileDialog::m_DrawValidationButtons() {
4096
0
    bool res = false;
4097
4098
0
    ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (ImGui::GetContentRegionAvail().x - prOkCancelButtonWidth) * okCancelButtonAlignement);
4099
4100
0
    ImGui::BeginGroup();
4101
4102
0
    if (invertOkAndCancelButtons) {
4103
0
        res |= m_DrawCancelButton();
4104
0
        res |= m_DrawOkButton();
4105
0
    } else {
4106
0
        res |= m_DrawOkButton();
4107
0
        res |= m_DrawCancelButton();
4108
0
    }
4109
4110
0
    ImGui::EndGroup();
4111
4112
0
    prOkCancelButtonWidth = ImGui::GetItemRectSize().x;
4113
4114
0
    return res;
4115
0
}
4116
4117
0
bool IGFD::FileDialog::m_DrawFooter() {
4118
0
    auto& fdFile = m_FileDialogInternal.fileManager;
4119
4120
0
    float posY = ImGui::GetCursorPos().y;  // height of last bar calc
4121
0
    ImGui::AlignTextToFramePadding();
4122
0
    if (!fdFile.dLGDirectoryMode)
4123
0
        ImGui::Text(fileNameString);
4124
0
    else  // directory chooser
4125
0
        ImGui::Text(dirNameString);
4126
0
    ImGui::SameLine();
4127
4128
    // Input file fields
4129
0
    float width = ImGui::GetContentRegionAvail().x;
4130
0
    if (!fdFile.dLGDirectoryMode) {
4131
0
        ImGuiContext& g = *GImGui;
4132
0
        width -= m_FileDialogInternal.filterManager.GetFilterComboBoxWidth() + g.Style.ItemSpacing.x;
4133
0
    }
4134
4135
0
    ImGui::PushItemWidth(width);
4136
0
    ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;
4137
0
    if (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ReadOnlyFileNameField) {
4138
0
        flags |= ImGuiInputTextFlags_ReadOnly;
4139
0
    }
4140
0
    if (ImGui::InputText("##FileName", fdFile.fileNameBuffer, MAX_FILE_DIALOG_NAME_BUFFER, flags)) {
4141
0
        m_FileDialogInternal.isOk = true;
4142
0
    }
4143
0
    if (ImGui::GetItemID() == ImGui::GetActiveID()) m_FileDialogInternal.fileInputIsActive = true;
4144
0
    ImGui::PopItemWidth();
4145
4146
    // combobox of filters
4147
0
    m_FileDialogInternal.filterManager.DrawFilterComboBox(m_FileDialogInternal);
4148
4149
0
    bool res                          = m_DrawValidationButtons();
4150
0
    m_FileDialogInternal.footerHeight = ImGui::GetCursorPosY() - posY;
4151
0
    return res;
4152
0
}
4153
4154
0
bool IGFD::FileDialog::m_Selectable(int vRowIdx, const char* vLabel, bool vSelected, ImGuiSelectableFlags vFlags, const ImVec2& vSizeArg) {
4155
0
    bool res = false;
4156
#ifdef USE_EXPLORATION_BY_KEYS
4157
    bool flashed = m_BeginFlashItem((size_t)vRowIdx);
4158
    res = m_FlashableSelectable(vLabel, vSelected, vFlags, flashed, vSizeArg);
4159
    if (flashed) {
4160
        m_EndFlashItem();
4161
    }
4162
#else   // USE_EXPLORATION_BY_KEYS
4163
0
    (void)vRowIdx;  // remove a warnings for unused var
4164
0
    res = ImGui::Selectable(vLabel, vSelected, vFlags, vSizeArg);
4165
0
#endif  // USE_EXPLORATION_BY_KEYS
4166
0
    return res;
4167
0
}
4168
4169
0
void IGFD::FileDialog::m_SelectableItem(int vRowIdx, std::shared_ptr<FileInfos> vInfos, bool vSelected, const char* vFmt, ...) {
4170
0
    if (!vInfos.use_count()) return;
4171
4172
0
    auto& fdi = m_FileDialogInternal.fileManager;
4173
4174
0
    static ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_SpanAvailWidth;
4175
4176
0
    va_list args;
4177
0
    va_start(args, vFmt);
4178
0
    vsnprintf(fdi.variadicBuffer, MAX_FILE_DIALOG_NAME_BUFFER, vFmt, args);
4179
0
    va_end(args);
4180
4181
0
    float h = 0.0f;
4182
#ifdef USE_THUMBNAILS
4183
    if (m_DisplayMode == DisplayModeEnum::THUMBNAILS_LIST && !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_DisableThumbnailMode)) {
4184
        h = DisplayMode_ThumbailsList_ImageHeight;
4185
    }
4186
#endif  // USE_THUMBNAILS
4187
0
    if (m_Selectable(vRowIdx, fdi.variadicBuffer, vSelected, selectableFlags, ImVec2(-1.0f, h))) {
4188
0
        if (vInfos->fileType.isDir()) {
4189
            // nav system, selectable cause open directory or select directory
4190
0
            if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) {
4191
                // little fix for get back the mouse behavior in nav system
4192
0
                if (ImGui::IsMouseDoubleClicked(0)) {  // 0 -> left mouse button double click
4193
0
                    fdi.pathClicked = fdi.SelectDirectory(vInfos);
4194
0
                } else if (fdi.dLGDirectoryMode) {  // directory chooser
4195
0
                    fdi.SelectOrDeselectFileName(m_FileDialogInternal, vInfos);
4196
0
                } else {
4197
0
                    fdi.pathClicked = fdi.SelectDirectory(vInfos);
4198
0
                }
4199
0
            } else {                                   // no nav system => classic behavior
4200
0
                if (ImGui::IsMouseDoubleClicked(0)) {  // 0 -> left mouse button double click
4201
0
                    fdi.pathClicked = fdi.SelectDirectory(vInfos);
4202
0
                } else if (fdi.dLGDirectoryMode) {  // directory chooser
4203
0
                    fdi.SelectOrDeselectFileName(m_FileDialogInternal, vInfos);
4204
0
                }
4205
0
            }
4206
0
        } else {
4207
0
            fdi.SelectOrDeselectFileName(m_FileDialogInternal, vInfos);
4208
0
            if (ImGui::IsMouseDoubleClicked(0)) {
4209
0
                m_FileDialogInternal.isOk = true;
4210
0
            }
4211
0
        }
4212
0
    }
4213
0
}
4214
4215
0
void IGFD::FileDialog::m_DisplayFileInfosTooltip(const int32_t& vRowIdx, const int32_t& vColumnIdx, std::shared_ptr<FileInfos> vFileInfos) {
4216
    // IsItemHovered is not sufficient since file size have two calls to Text
4217
0
    if ((ImGui::TableGetHoveredColumn() == vColumnIdx) &&  // column hovered
4218
0
        (ImGui::TableGetHoveredRow() == (vRowIdx + 1)) &&  // row hovered
4219
0
        (vFileInfos != nullptr) &&                         // fileinfo not null
4220
0
        (vFileInfos->tooltipColumn == vColumnIdx) &&       // good tooltip column
4221
0
        (!vFileInfos->tooltipMessage.empty())) {           // tooltip not empty
4222
0
        ImGui::SetTooltip("%s", vFileInfos->tooltipMessage.c_str());
4223
0
    }
4224
0
}
4225
4226
0
void IGFD::FileDialog::m_BeginFileColorIconStyle(std::shared_ptr<FileInfos> vFileInfos, bool& vOutShowColor, std::string& vOutStr, ImFont** vOutFont) {
4227
0
    vOutStr.clear();
4228
0
    vOutShowColor = false;
4229
4230
0
    if (vFileInfos->fileStyle != nullptr) {
4231
0
        vOutShowColor = true;
4232
0
        *vOutFont     = vFileInfos->fileStyle->font;
4233
0
    }
4234
4235
0
    if (vOutShowColor && !vFileInfos->fileStyle->icon.empty()) {
4236
0
        vOutStr = vFileInfos->fileStyle->icon;
4237
0
    } else if (vFileInfos->fileType.isDir()) {
4238
0
        vOutStr = dirEntryString;
4239
0
    } else if (vFileInfos->fileType.isLinkToUnknown()) {
4240
0
        vOutStr = linkEntryString;
4241
0
    } else if (vFileInfos->fileType.isFile()) {
4242
0
        vOutStr = fileEntryString;
4243
0
    }
4244
4245
0
    vOutStr += " " + vFileInfos->fileNameExt;
4246
4247
0
    if (vOutShowColor) {
4248
0
        ImGui::PushStyleColor(ImGuiCol_Text, vFileInfos->fileStyle->color);
4249
0
    }
4250
0
    if (*vOutFont) {
4251
#if IMGUI_VERSION_NUM < 19201
4252
        ImGui::PushFont(*vOutFont);
4253
#else
4254
0
        ImGui::PushFont(*vOutFont, 0.0f);
4255
0
#endif
4256
0
    }
4257
0
}
4258
4259
0
void IGFD::FileDialog::m_EndFileColorIconStyle(const bool vShowColor, ImFont* vFont) {
4260
0
    if (vFont) {
4261
0
        ImGui::PopFont();
4262
0
    }
4263
0
    if (vShowColor) {
4264
0
        ImGui::PopStyleColor();
4265
0
    }
4266
0
}
4267
4268
0
void IGFD::FileDialog::m_drawColumnText(int /*vColIdx*/, const char* vFmt, const char* vLabel, bool /*vSelected*/, bool /*vHovered*/) {
4269
0
    ImGui::Text(vFmt, vLabel);
4270
0
}
4271
4272
0
void IGFD::FileDialog::m_rightAlignText(const char* text, const char* maxWidthText) {
4273
0
    const auto maxWidth    = ImGui::CalcTextSize(maxWidthText).x;
4274
0
    const auto actualWidth = ImGui::CalcTextSize(text).x;
4275
0
    const auto spacing     = maxWidth - actualWidth;
4276
0
    if (spacing > 0.0f) {
4277
0
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() + spacing);
4278
0
    }
4279
0
    ImGui::TextUnformatted(text);
4280
0
}
4281
4282
0
void IGFD::FileDialog::m_DrawFileListView(ImVec2 vSize) {
4283
0
    auto& fdi = m_FileDialogInternal.fileManager;
4284
4285
0
    ImGui::PushID(this);
4286
4287
0
    static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_NoHostExtendY
4288
0
#ifndef USE_CUSTOM_SORTING_ICON
4289
0
                                   | ImGuiTableFlags_Sortable
4290
0
#endif  // USE_CUSTOM_SORTING_ICON
4291
0
        ;
4292
0
    const auto listViewID = ImGui::GetID("FileTable");
4293
0
    if (ImGui::BeginTableEx("FileTable", listViewID, 4, flags, vSize, 0.0f)) {
4294
0
        ImGui::TableSetupScrollFreeze(0, 1);  // Make header always visible
4295
0
        ImGui::TableSetupColumn(fdi.headerFileName.c_str(), ImGuiTableColumnFlags_WidthStretch | (defaultSortOrderFilename ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 0);
4296
0
        ImGui::TableSetupColumn(fdi.headerFileType.c_str(),
4297
0
                                ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderType ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4298
0
                                    ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0),
4299
0
                                -1, 1);
4300
0
        ImGui::TableSetupColumn(fdi.headerFileSize.c_str(),
4301
0
                                ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderSize ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4302
0
                                    ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0),
4303
0
                                -1, 2);
4304
0
        ImGui::TableSetupColumn(fdi.headerFileDate.c_str(),
4305
0
                                ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderDate ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4306
0
                                    ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0),
4307
0
                                -1, 3);
4308
4309
0
#ifndef USE_CUSTOM_SORTING_ICON
4310
        // Sort our data if sort specs have been changed!
4311
0
        if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) {
4312
0
            if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) {
4313
0
                bool direction = sorts_specs->Specs->SortDirection == ImGuiSortDirection_Ascending;
4314
4315
0
                if (sorts_specs->Specs->ColumnUserID == 0) {
4316
0
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
4317
0
                    fdi.sortingDirection[0] = direction;
4318
0
                    fdi.SortFields(m_FileDialogInternal);
4319
0
                } else if (sorts_specs->Specs->ColumnUserID == 1) {
4320
0
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
4321
0
                    fdi.sortingDirection[1] = direction;
4322
0
                    fdi.SortFields(m_FileDialogInternal);
4323
0
                } else if (sorts_specs->Specs->ColumnUserID == 2) {
4324
0
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
4325
0
                    fdi.sortingDirection[2] = direction;
4326
0
                    fdi.SortFields(m_FileDialogInternal);
4327
0
                } else  // if (sorts_specs->Specs->ColumnUserID == 3) => alwayd true for the moment, to uncomment if we
4328
                        // add a fourth column
4329
0
                {
4330
0
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
4331
0
                    fdi.sortingDirection[3] = direction;
4332
0
                    fdi.SortFields(m_FileDialogInternal);
4333
0
                }
4334
4335
0
                sorts_specs->SpecsDirty = false;
4336
0
            }
4337
0
        }
4338
4339
0
        ImGui::TableHeadersRow();
4340
#else   // USE_CUSTOM_SORTING_ICON
4341
        ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
4342
        for (int column = 0; column < 4; column++)  //-V112
4343
        {
4344
            ImGui::TableSetColumnIndex(column);
4345
            const char* column_name = ImGui::TableGetColumnName(column);  // Retrieve name passed to TableSetupColumn()
4346
            ImGui::PushID(column);
4347
            ImGui::TableHeader(column_name);
4348
            ImGui::PopID();
4349
            if (ImGui::IsItemClicked()) {
4350
                if (column == 0) {
4351
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME)
4352
                        fdi.sortingDirection[0] = !fdi.sortingDirection[0];
4353
                    else
4354
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
4355
4356
                    fdi.SortFields(m_FileDialogInternal);
4357
                } else if (column == 1) {
4358
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_TYPE)
4359
                        fdi.sortingDirection[1] = !fdi.sortingDirection[1];
4360
                    else
4361
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
4362
4363
                    fdi.SortFields(m_FileDialogInternal);
4364
                } else if (column == 2) {
4365
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_SIZE)
4366
                        fdi.sortingDirection[2] = !fdi.sortingDirection[2];
4367
                    else
4368
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
4369
4370
                    fdi.SortFields(m_FileDialogInternal);
4371
                } else  // if (column == 3) => alwayd true for the moment, to uncomment if we add a fourth column
4372
                {
4373
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_DATE)
4374
                        fdi.sortingDirection[3] = !fdi.sortingDirection[3];
4375
                    else
4376
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
4377
4378
                    fdi.SortFields(m_FileDialogInternal);
4379
                }
4380
            }
4381
        }
4382
#endif  // USE_CUSTOM_SORTING_ICON
4383
0
        if (!fdi.IsFilteredListEmpty()) {
4384
0
            std::string _str;
4385
0
            ImFont* _font   = nullptr;
4386
0
            bool _showColor = false;
4387
4388
0
            int column_id = 0;
4389
0
            bool _rowHovered = false;
4390
0
            m_FileListClipper.Begin((int)fdi.GetFilteredListSize(), ImGui::GetTextLineHeightWithSpacing());
4391
0
            while (m_FileListClipper.Step()) {
4392
0
                for (int i = m_FileListClipper.DisplayStart; i < m_FileListClipper.DisplayEnd; i++) {
4393
0
                    if (i < 0) {
4394
0
                        continue;
4395
0
                    }
4396
4397
0
                    auto pInfos = fdi.GetFilteredFileAt((size_t)i);
4398
0
                    if (pInfos == nullptr) {
4399
0
                        continue;
4400
0
                    }
4401
4402
0
                    m_BeginFileColorIconStyle(pInfos, _showColor, _str, &_font);
4403
4404
0
                    const bool selected = fdi.IsFileNameSelected(pInfos->fileNameExt);  // found
4405
4406
0
                    ImGui::TableNextRow();
4407
4408
0
                    column_id = 0;
4409
0
                    _rowHovered = false;
4410
0
                    if (ImGui::TableNextColumn()) {  // file name
4411
0
                        if (!pInfos->deviceInfos.empty()) {
4412
0
                            _str += " " + pInfos->deviceInfos;
4413
0
                        }
4414
0
                        m_SelectableItem(i, pInfos, selected, _str.c_str());
4415
0
                        _rowHovered = ImGui::IsItemHovered();
4416
0
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4417
0
                    }
4418
0
                    column_id++;                     // some columns can be hidden, but the id must keep good
4419
0
                    if (ImGui::TableNextColumn()) {  // file type
4420
0
                        m_drawColumnText(column_id, "%s", pInfos->fileExtLevels[0].c_str(), selected, _rowHovered);
4421
0
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4422
0
                    }
4423
0
                    column_id++;                     // some columns can be hidden, but the id must keep good
4424
0
                    if (ImGui::TableNextColumn()) {  // file size
4425
0
                        if (!pInfos->fileType.isDir()) {
4426
0
                            m_rightAlignText(pInfos->formatedFileSize.first.c_str(), "9999.99");
4427
0
                            ImGui::SameLine(0.0f, 0.0f);
4428
0
                            m_drawColumnText(column_id, " %s ", pInfos->formatedFileSize.second.c_str(), selected, _rowHovered);
4429
0
                        } else {
4430
0
                            ImGui::TextUnformatted("");
4431
0
                        }
4432
0
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4433
0
                    }
4434
0
                    column_id++;                     // some columns can be hidden, but the id must keep good
4435
0
                    if (ImGui::TableNextColumn()) {  // file date + time
4436
0
                        m_drawColumnText(column_id, "%s", pInfos->fileModifDate.c_str(), selected, _rowHovered);
4437
0
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4438
0
                    }
4439
0
                    m_EndFileColorIconStyle(_showColor, _font);
4440
0
                }
4441
0
            }
4442
0
            m_FileListClipper.End();
4443
0
        }
4444
4445
#ifdef USE_EXPLORATION_BY_KEYS
4446
        if (!fdi.inputPathActivated) {
4447
            m_LocateByInputKey(m_FileDialogInternal);
4448
            m_ExploreWithkeys(m_FileDialogInternal, listViewID);
4449
        }
4450
#endif  // USE_EXPLORATION_BY_KEYS
4451
4452
0
        ImGuiContext& g = *GImGui;
4453
0
        if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) {
4454
0
            m_FileDialogInternal.fileListViewIsActive = true;
4455
0
        }
4456
4457
0
        ImGui::EndTable();
4458
0
    }
4459
4460
0
    ImGui::PopID();
4461
0
}
4462
4463
#ifdef USE_THUMBNAILS
4464
void IGFD::FileDialog::m_DrawThumbnailsListView(ImVec2 vSize) {
4465
    auto& fdi = m_FileDialogInternal.fileManager;
4466
4467
    ImGui::PushID(this);
4468
4469
    static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_NoHostExtendY
4470
#ifndef USE_CUSTOM_SORTING_ICON
4471
                                   | ImGuiTableFlags_Sortable
4472
#endif  // USE_CUSTOM_SORTING_ICON
4473
        ;
4474
    auto listViewID = ImGui::GetID("##FileDialog_fileTable");
4475
    if (ImGui::BeginTableEx("##FileDialog_fileTable", listViewID, 5, flags, vSize, 0.0f)) {
4476
        ImGui::TableSetupScrollFreeze(0, 1);  // Make header always visible
4477
        ImGui::TableSetupColumn(fdi.headerFileName.c_str(), ImGuiTableColumnFlags_WidthStretch | (defaultSortOrderFilename ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 0);
4478
        ImGui::TableSetupColumn(fdi.headerFileType.c_str(),
4479
                                ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderType ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4480
                                    ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnType) ? ImGuiTableColumnFlags_DefaultHide : 0),
4481
                                -1, 1);
4482
        ImGui::TableSetupColumn(fdi.headerFileSize.c_str(),
4483
                                ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderSize ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4484
                                    ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnSize) ? ImGuiTableColumnFlags_DefaultHide : 0),
4485
                                -1, 2);
4486
        ImGui::TableSetupColumn(fdi.headerFileDate.c_str(),
4487
                                ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderDate ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending) |
4488
                                    ((m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_HideColumnDate) ? ImGuiTableColumnFlags_DefaultHide : 0),
4489
                                -1, 3);
4490
        // not needed to have an option for hide the thumbnails since this is why this view is used
4491
        ImGui::TableSetupColumn(fdi.headerFileThumbnails.c_str(), ImGuiTableColumnFlags_WidthFixed | (defaultSortOrderThumbnails ? ImGuiTableColumnFlags_PreferSortAscending : ImGuiTableColumnFlags_PreferSortDescending), -1, 4);  //-V112
4492
4493
#ifndef USE_CUSTOM_SORTING_ICON
4494
        // Sort our data if sort specs have been changed!
4495
        if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) {
4496
            if (sorts_specs->SpecsDirty && !fdi.IsFileListEmpty()) {
4497
                bool direction = sorts_specs->Specs->SortDirection == ImGuiSortDirection_Ascending;
4498
4499
                if (sorts_specs->Specs->ColumnUserID == 0) {
4500
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
4501
                    fdi.sortingDirection[0] = direction;
4502
                    fdi.SortFields(m_FileDialogInternal);
4503
                } else if (sorts_specs->Specs->ColumnUserID == 1) {
4504
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
4505
                    fdi.sortingDirection[1] = direction;
4506
                    fdi.SortFields(m_FileDialogInternal);
4507
                } else if (sorts_specs->Specs->ColumnUserID == 2) {
4508
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
4509
                    fdi.sortingDirection[2] = direction;
4510
                    fdi.SortFields(m_FileDialogInternal);
4511
                } else if (sorts_specs->Specs->ColumnUserID == 3) {
4512
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
4513
                    fdi.sortingDirection[3] = direction;
4514
                    fdi.SortFields(m_FileDialogInternal);
4515
                } else  // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we
4516
                        // add another column
4517
                {
4518
                    fdi.sortingField        = IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS;
4519
                    fdi.sortingDirection[4] = direction;
4520
                    fdi.SortFields(m_FileDialogInternal);
4521
                }
4522
4523
                sorts_specs->SpecsDirty = false;
4524
            }
4525
        }
4526
4527
        ImGui::TableHeadersRow();
4528
#else   // USE_CUSTOM_SORTING_ICON
4529
        ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
4530
        for (int column = 0; column < 5; column++) {
4531
            ImGui::TableSetColumnIndex(column);
4532
            const char* column_name = ImGui::TableGetColumnName(column);  // Retrieve name passed to TableSetupColumn()
4533
            ImGui::PushID(column);
4534
            ImGui::TableHeader(column_name);
4535
            ImGui::PopID();
4536
            if (ImGui::IsItemClicked()) {
4537
                if (column == 0) {
4538
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME)
4539
                        fdi.sortingDirection[0] = !fdi.sortingDirection[0];
4540
                    else
4541
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_FILENAME;
4542
4543
                    fdi.SortFields(m_FileDialogInternal);
4544
                } else if (column == 1) {
4545
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_TYPE)
4546
                        fdi.sortingDirection[1] = !fdi.sortingDirection[1];
4547
                    else
4548
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_TYPE;
4549
4550
                    fdi.SortFields(m_FileDialogInternal);
4551
                } else if (column == 2) {
4552
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_SIZE)
4553
                        fdi.sortingDirection[2] = !fdi.sortingDirection[2];
4554
                    else
4555
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_SIZE;
4556
4557
                    fdi.SortFields(m_FileDialogInternal);
4558
                } else if (column == 3) {
4559
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_DATE)
4560
                        fdi.sortingDirection[3] = !fdi.sortingDirection[3];
4561
                    else
4562
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_DATE;
4563
4564
                    fdi.SortFields(m_FileDialogInternal);
4565
                } else  // if (sorts_specs->Specs->ColumnUserID == 4) = > always true for the moment, to uncomment if we
4566
                        // add another column
4567
                {
4568
                    if (fdi.sortingField == IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS)
4569
                        fdi.sortingDirection[4] = !fdi.sortingDirection[4];
4570
                    else
4571
                        fdi.sortingField = IGFD::FileManager::SortingFieldEnum::FIELD_THUMBNAILS;
4572
4573
                    fdi.SortFields(m_FileDialogInternal);
4574
                }
4575
            }
4576
        }
4577
#endif  // USE_CUSTOM_SORTING_ICON
4578
        if (!fdi.IsFilteredListEmpty()) {
4579
            std::string _str;
4580
            ImFont* _font   = nullptr;
4581
            bool _showColor = false;
4582
4583
            ImGuiContext& g        = *GImGui;
4584
            const float itemHeight = ImMax(g.FontSize, DisplayMode_ThumbailsList_ImageHeight) + g.Style.ItemSpacing.y;
4585
4586
            int column_id = 0;
4587
            m_FileListClipper.Begin((int)fdi.GetFilteredListSize(), itemHeight);
4588
            while (m_FileListClipper.Step()) {
4589
                for (int i = m_FileListClipper.DisplayStart; i < m_FileListClipper.DisplayEnd; i++) {
4590
                    if (i < 0) continue;
4591
4592
                    auto pInfos = fdi.GetFilteredFileAt((size_t)i);
4593
                    if (!pInfos.use_count()) continue;
4594
4595
                    m_BeginFileColorIconStyle(pInfos, _showColor, _str, &_font);
4596
4597
                    bool selected = fdi.IsFileNameSelected(pInfos->fileNameExt);  // found
4598
4599
                    ImGui::TableNextRow();
4600
4601
                    column_id = 0;
4602
                    if (ImGui::TableNextColumn()) {  // file name
4603
                        if (!pInfos->deviceInfos.empty()) {
4604
                            _str += " " + pInfos->deviceInfos;
4605
                        }
4606
                        m_SelectableItem(i, pInfos, selected, _str.c_str());
4607
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4608
                    }
4609
                    column_id++;                     // some columns can be hidden, but the id must keep good
4610
                    if (ImGui::TableNextColumn()) {  // file type
4611
                        ImGui::Text("%s", pInfos->fileExtLevels[0].c_str());
4612
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4613
                    }
4614
                    column_id++;                     // some columns can be hidden, but the id must keep good
4615
                    if (ImGui::TableNextColumn()) {  // file size
4616
                        if (!pInfos->fileType.isDir()) {
4617
                            m_rightAlignText(pInfos->formatedFileSize.first.c_str(), "9999.99");
4618
                            ImGui::SameLine(0.0f, 0.0f);
4619
                            ImGui::Text(" %s ", pInfos->formatedFileSize.second.c_str());
4620
                        } else {
4621
                            ImGui::TextUnformatted("");
4622
                        }
4623
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4624
                    }
4625
                    column_id++;                     // some columns can be hidden, but the id must keep good
4626
                    if (ImGui::TableNextColumn()) {  // file date + time
4627
                        ImGui::Text("%s", pInfos->fileModifDate.c_str());
4628
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4629
                    }
4630
                    column_id++;                     // some columns can be hidden, but the id must keep good
4631
                    if (ImGui::TableNextColumn()) {  // file thumbnails
4632
                        auto th = &pInfos->thumbnailInfo;
4633
4634
                        if (!th->isLoadingOrLoaded) {
4635
                            m_AddThumbnailToLoad(pInfos);
4636
                        }
4637
                        if (th->isReadyToDisplay && th->textureID) {
4638
                            ImGui::Image((ImTextureID)th->textureID, ImVec2((float)th->textureWidth, (float)th->textureHeight));
4639
                        }
4640
                        m_DisplayFileInfosTooltip(i, column_id, pInfos);
4641
                    }
4642
4643
                    m_EndFileColorIconStyle(_showColor, _font);
4644
                }
4645
            }
4646
            m_FileListClipper.End();
4647
        }
4648
4649
#ifdef USE_EXPLORATION_BY_KEYS
4650
        if (!fdi.inputPathActivated) {
4651
            m_LocateByInputKey(m_FileDialogInternal);
4652
            m_ExploreWithkeys(m_FileDialogInternal, listViewID);
4653
        }
4654
#endif  // USE_EXPLORATION_BY_KEYS
4655
4656
        ImGuiContext& g = *GImGui;
4657
        if (g.LastActiveId - 1 == listViewID || g.LastActiveId == listViewID) {
4658
            m_FileDialogInternal.fileListViewIsActive = true;
4659
        }
4660
4661
        ImGui::EndTable();
4662
    }
4663
4664
    ImGui::PopID();
4665
}
4666
4667
void IGFD::FileDialog::m_DrawThumbnailsGridView(ImVec2 vSize) {
4668
    if (ImGui::BeginChild("##thumbnailsGridsFiles", vSize)) {
4669
        // todo
4670
    }
4671
4672
    ImGui::EndChild();
4673
}
4674
4675
#endif
4676
4677
0
void IGFD::FileDialog::m_DrawSidePane(float vHeight) {
4678
0
    ImGui::SameLine();
4679
4680
0
    ImGui::BeginChild("##FileTypes", ImVec2(0, vHeight));
4681
4682
0
    m_FileDialogInternal.getDialogConfig().sidePane(m_FileDialogInternal.filterManager.GetSelectedFilter().getFirstFilter().c_str(), m_FileDialogInternal.getDialogConfigRef().userDatas, &m_FileDialogInternal.canWeContinue);
4683
0
    ImGui::EndChild();
4684
0
}
4685
4686
0
void IGFD::FileDialog::Close() {
4687
0
    m_FileDialogInternal.dLGkey.clear();
4688
0
    m_FileDialogInternal.showDialog = false;
4689
0
}
4690
4691
0
bool IGFD::FileDialog::WasOpenedThisFrame(const std::string& vKey) const {
4692
0
    bool res = m_FileDialogInternal.showDialog && m_FileDialogInternal.dLGkey == vKey;
4693
0
    if (res) {
4694
0
        res &= m_FileDialogInternal.lastImGuiFrameCount == GImGui->FrameCount;  // return true if a dialog was displayed in this frame
4695
0
    }
4696
0
    return res;
4697
0
}
4698
4699
0
bool IGFD::FileDialog::WasOpenedThisFrame() const {
4700
0
    bool res = m_FileDialogInternal.showDialog;
4701
0
    if (res) {
4702
0
        res &= m_FileDialogInternal.lastImGuiFrameCount == GImGui->FrameCount;  // return true if a dialog was displayed in this frame
4703
0
    }
4704
0
    return res;
4705
0
}
4706
4707
0
bool IGFD::FileDialog::IsOpened(const std::string& vKey) const {
4708
0
    return (m_FileDialogInternal.showDialog && m_FileDialogInternal.dLGkey == vKey);
4709
0
}
4710
4711
0
bool IGFD::FileDialog::IsOpened() const {
4712
0
    return m_FileDialogInternal.showDialog;
4713
0
}
4714
4715
0
std::string IGFD::FileDialog::GetOpenedKey() const {
4716
0
    if (m_FileDialogInternal.showDialog) {
4717
0
        return m_FileDialogInternal.dLGkey;
4718
0
    }
4719
0
    return "";
4720
0
}
4721
4722
0
std::string IGFD::FileDialog::GetFilePathName(IGFD_ResultMode vFlag) {
4723
0
    return m_FileDialogInternal.fileManager.GetResultingFilePathName(m_FileDialogInternal, vFlag);
4724
0
}
4725
4726
0
std::string IGFD::FileDialog::GetCurrentPath() {
4727
0
    return m_FileDialogInternal.fileManager.GetResultingPath();
4728
0
}
4729
4730
0
std::string IGFD::FileDialog::GetCurrentFileName(IGFD_ResultMode vFlag) {
4731
0
    return m_FileDialogInternal.fileManager.GetResultingFileName(m_FileDialogInternal, vFlag);
4732
0
}
4733
4734
0
std::string IGFD::FileDialog::GetCurrentFilter() {
4735
0
    return m_FileDialogInternal.filterManager.GetSelectedFilter().title;
4736
0
}
4737
4738
0
std::map<std::string, std::string> IGFD::FileDialog::GetSelection(IGFD_ResultMode vFlag) {
4739
0
    return m_FileDialogInternal.fileManager.GetResultingSelection(m_FileDialogInternal, vFlag);
4740
0
}
4741
4742
0
IGFD::UserDatas IGFD::FileDialog::GetUserDatas() const {
4743
0
    return m_FileDialogInternal.getDialogConfig().userDatas;
4744
0
}
4745
4746
0
bool IGFD::FileDialog::IsOk() const {
4747
0
    return m_FileDialogInternal.isOk;
4748
0
}
4749
4750
0
void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos) {
4751
0
    m_FileDialogInternal.filterManager.SetFileStyle(vFlags, vCriteria, vInfos);
4752
0
}
4753
4754
0
void IGFD::FileDialog::SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon, ImFont* vFont) {
4755
0
    m_FileDialogInternal.filterManager.SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
4756
0
}
4757
4758
0
void IGFD::FileDialog::SetFileStyle(FileStyle::FileStyleFunctor vFunctor) {
4759
0
    m_FileDialogInternal.filterManager.SetFileStyle(vFunctor);
4760
0
}
4761
4762
0
bool IGFD::FileDialog::GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon, ImFont** vOutFont) {
4763
0
    return m_FileDialogInternal.filterManager.GetFileStyle(vFlags, vCriteria, vOutColor, vOutIcon, vOutFont);
4764
0
}
4765
4766
0
void IGFD::FileDialog::ClearFilesStyle() {
4767
0
    m_FileDialogInternal.filterManager.ClearFilesStyle();
4768
0
}
4769
4770
0
void IGFD::FileDialog::SetLocales(const int& /*vLocaleCategory*/, const std::string& vLocaleBegin, const std::string& vLocaleEnd) {
4771
0
    m_FileDialogInternal.puUseCustomLocale = true;
4772
0
    m_FileDialogInternal.localeBegin       = vLocaleBegin;
4773
0
    m_FileDialogInternal.localeEnd         = vLocaleEnd;
4774
0
}
4775
4776
//////////////////////////////////////////////////////////////////////////////
4777
//// OVERWRITE DIALOG ////////////////////////////////////////////////////////
4778
//////////////////////////////////////////////////////////////////////////////
4779
4780
0
bool IGFD::FileDialog::m_Confirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags) {
4781
    // if confirmation => return true for confirm the overwrite et quit the dialog
4782
    // if cancel => return false && set IsOk to false for keep inside the dialog
4783
4784
    // if IsOk == false => return false for quit the dialog
4785
0
    if (!m_FileDialogInternal.isOk && vLastAction) {
4786
0
        m_QuitFrame();
4787
0
        return true;
4788
0
    }
4789
4790
    // if IsOk == true && no check of overwrite => return true for confirm the dialog
4791
0
    if (m_FileDialogInternal.isOk && vLastAction && !(m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ConfirmOverwrite)) {
4792
0
        m_QuitFrame();
4793
0
        return true;
4794
0
    }
4795
4796
    // if IsOk == true && check of overwrite => return false and show confirm to overwrite dialog
4797
0
    if ((m_FileDialogInternal.okResultToConfirm || (m_FileDialogInternal.isOk && vLastAction)) && (m_FileDialogInternal.getDialogConfig().flags & ImGuiFileDialogFlags_ConfirmOverwrite)) {
4798
0
        if (m_FileDialogInternal.isOk)  // catched only one time
4799
0
        {
4800
0
            if (!m_FileDialogInternal.fileManager.GetFileSystemInstance()->IsFileExist(GetFilePathName()))  // not existing => quit dialog
4801
0
            {
4802
0
                m_QuitFrame();
4803
0
                return true;
4804
0
            } else  // existing => confirm dialog to open
4805
0
            {
4806
0
                m_FileDialogInternal.isOk              = false;
4807
0
                m_FileDialogInternal.okResultToConfirm = true;
4808
0
            }
4809
0
        }
4810
4811
0
        std::string name = OverWriteDialogTitleString "##" + m_FileDialogInternal.dLGtitle + m_FileDialogInternal.dLGkey + "OverWriteDialog";
4812
4813
0
        bool res = false;
4814
4815
0
        ImGui::OpenPopup(name.c_str());
4816
0
        if (ImGui::BeginPopupModal(name.c_str(), (bool*)0, vFlags | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
4817
0
            ImGui::SetWindowPos(m_FileDialogInternal.dialogCenterPos - ImGui::GetWindowSize() * 0.5f);  // next frame needed for GetWindowSize to work
4818
4819
0
            ImGui::Text("%s", OverWriteDialogMessageString);
4820
4821
0
            if (IMGUI_BUTTON(OverWriteDialogConfirmButtonString)) {
4822
0
                m_FileDialogInternal.okResultToConfirm = false;
4823
0
                m_FileDialogInternal.isOk              = true;
4824
0
                res                                    = true;
4825
0
                ImGui::CloseCurrentPopup();
4826
0
            }
4827
4828
0
            ImGui::SameLine();
4829
4830
0
            if (IMGUI_BUTTON(OverWriteDialogCancelButtonString)) {
4831
0
                m_FileDialogInternal.okResultToConfirm = false;
4832
0
                m_FileDialogInternal.isOk              = false;
4833
0
                res                                    = false;
4834
0
                ImGui::CloseCurrentPopup();
4835
0
            }
4836
4837
0
            ImGui::EndPopup();
4838
0
        }
4839
4840
0
        if (res) {
4841
0
            m_QuitFrame();
4842
0
        }
4843
0
        return res;
4844
0
    }
4845
4846
0
    return false;
4847
0
}
4848
4849
#endif  // __cplusplus
4850
4851
// return an initialized IGFD_FileDialog_Config
4852
0
IGFD_C_API IGFD_FileDialog_Config IGFD_FileDialog_Config_Get() {
4853
0
    IGFD_FileDialog_Config res = {};
4854
0
    res.path                   = "";
4855
0
    res.fileName               = "";
4856
0
    res.filePathName           = "";
4857
0
    res.countSelectionMax      = 1;
4858
0
    res.userDatas              = nullptr;
4859
0
    res.sidePane               = nullptr;
4860
0
    res.sidePaneWidth          = 250.0f;
4861
0
    res.flags                  = ImGuiFileDialogFlags_Default;
4862
0
    return res;
4863
0
}
4864
4865
// Return an initialized IGFD_Selection_Pair
4866
0
IGFD_C_API IGFD_Selection_Pair IGFD_Selection_Pair_Get(void) {
4867
0
    IGFD_Selection_Pair res = {};
4868
0
    res.fileName            = nullptr;
4869
0
    res.filePathName        = nullptr;
4870
0
    return res;
4871
0
}
4872
4873
// destroy only the content of vSelection_Pair
4874
0
IGFD_C_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair) {
4875
0
    if (vSelection_Pair) {
4876
0
        delete[] vSelection_Pair->fileName;
4877
0
        delete[] vSelection_Pair->filePathName;
4878
0
    }
4879
0
}
4880
4881
// Return an initialized IGFD_Selection
4882
0
IGFD_C_API IGFD_Selection IGFD_Selection_Get(void) {
4883
0
    return {nullptr, 0U};
4884
0
}
4885
4886
// destroy only the content of vSelection
4887
0
IGFD_C_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection) {
4888
0
    if (vSelection) {
4889
0
        if (vSelection->table) {
4890
0
            for (size_t i = 0U; i < vSelection->count; i++) {
4891
0
                IGFD_Selection_Pair_DestroyContent(&vSelection->table[i]);
4892
0
            }
4893
0
            delete[] vSelection->table;
4894
0
        }
4895
0
        vSelection->count = 0U;
4896
0
    }
4897
0
}
4898
4899
// create an instance of ImGuiFileDialog
4900
0
IGFD_C_API ImGuiFileDialog* IGFD_Create(void) {
4901
0
    return new ImGuiFileDialog();
4902
0
}
4903
4904
// destroy the instance of ImGuiFileDialog
4905
0
IGFD_C_API void IGFD_Destroy(ImGuiFileDialog* vContextPtr) {
4906
0
    if (vContextPtr != nullptr) {
4907
0
        delete vContextPtr;
4908
0
        vContextPtr = nullptr;
4909
0
    }
4910
0
}
4911
4912
IGFD_C_API void IGFD_OpenDialog(             // open a standard dialog
4913
    ImGuiFileDialog* vContextPtr,            // ImGuiFileDialog context
4914
    const char* vKey,                        // key dialog
4915
    const char* vTitle,                      // title
4916
    const char* vFilters,                    // filters/filter collections. set it to null for directory mode
4917
0
    const IGFD_FileDialog_Config vConfig) {  // path
4918
0
    if (vContextPtr != nullptr) {
4919
0
        IGFD::FileDialogConfig config;
4920
0
        config.path              = vConfig.path;
4921
0
        config.fileName          = vConfig.fileName;
4922
0
        config.filePathName      = vConfig.filePathName;
4923
0
        config.countSelectionMax = vConfig.countSelectionMax;
4924
0
        config.userDatas         = vConfig.userDatas;
4925
0
        config.flags             = vConfig.flags;
4926
0
        config.sidePane          = vConfig.sidePane;
4927
0
        config.sidePaneWidth     = vConfig.sidePaneWidth;
4928
0
        vContextPtr->OpenDialog(vKey, vTitle, vFilters, config);
4929
0
    }
4930
0
}
4931
4932
0
IGFD_C_API bool IGFD_DisplayDialog(ImGuiFileDialog* vContextPtr, const char* vKey, ImGuiWindowFlags vFlags, ImVec2 vMinSize, ImVec2 vMaxSize) {
4933
0
    if (vContextPtr != nullptr) {
4934
0
        return vContextPtr->Display(vKey, vFlags, vMinSize, vMaxSize);
4935
0
    }
4936
0
    return false;
4937
0
}
4938
4939
0
IGFD_C_API void IGFD_CloseDialog(ImGuiFileDialog* vContextPtr) {
4940
0
    if (vContextPtr != nullptr) {
4941
0
        vContextPtr->Close();
4942
0
    }
4943
0
}
4944
4945
0
IGFD_C_API bool IGFD_IsOk(ImGuiFileDialog* vContextPtr) {
4946
0
    if (vContextPtr != nullptr) {
4947
0
        return vContextPtr->IsOk();
4948
0
    }
4949
0
    return false;
4950
0
}
4951
4952
0
IGFD_C_API bool IGFD_WasKeyOpenedThisFrame(ImGuiFileDialog* vContextPtr, const char* vKey) {
4953
0
    if (vContextPtr != nullptr) {
4954
0
        return vContextPtr->WasOpenedThisFrame(vKey);
4955
0
    }
4956
0
    return false;
4957
0
}
4958
4959
0
IGFD_C_API bool IGFD_WasOpenedThisFrame(ImGuiFileDialog* vContextPtr) {
4960
0
    if (vContextPtr != nullptr) {
4961
0
        return vContextPtr->WasOpenedThisFrame();
4962
0
    }
4963
4964
0
    return false;
4965
0
}
4966
4967
0
IGFD_C_API bool IGFD_IsKeyOpened(ImGuiFileDialog* vContextPtr, const char* vCurrentOpenedKey) {
4968
0
    if (vContextPtr != nullptr) {
4969
0
        return vContextPtr->IsOpened(vCurrentOpenedKey);
4970
0
    }
4971
4972
0
    return false;
4973
0
}
4974
4975
0
IGFD_C_API bool IGFD_IsOpened(ImGuiFileDialog* vContextPtr) {
4976
0
    if (vContextPtr != nullptr) {
4977
0
        return vContextPtr->IsOpened();
4978
0
    }
4979
4980
0
    return false;
4981
0
}
4982
4983
0
IGFD_C_API IGFD_Selection IGFD_GetSelection(ImGuiFileDialog* vContextPtr, IGFD_ResultMode vMode) {
4984
0
    IGFD_Selection res = IGFD_Selection_Get();
4985
0
    if (vContextPtr != nullptr) {
4986
0
        auto sel = vContextPtr->GetSelection(vMode);
4987
0
        if (!sel.empty()) {
4988
0
            res.count = sel.size();
4989
0
            res.table = new IGFD_Selection_Pair[res.count];
4990
4991
0
            size_t idx = 0U;
4992
0
            for (const auto& s : sel) {
4993
0
                IGFD_Selection_Pair* pair = res.table + idx++;
4994
4995
                // fileNameExt
4996
0
                if (!s.first.empty()) {
4997
0
                    size_t siz     = s.first.size() + 1U;
4998
0
                    pair->fileName = new char[siz];
4999
0
#ifndef _MSC_VER
5000
0
                    strncpy(pair->fileName, s.first.c_str(), siz);
5001
#else   // _MSC_VER
5002
                    strncpy_s(pair->fileName, siz, s.first.c_str(), siz);
5003
#endif  // _MSC_VER
5004
0
                    pair->fileName[siz - 1U] = '\0';
5005
0
                }
5006
5007
                // filePathName
5008
0
                if (!s.second.empty()) {
5009
0
                    size_t siz         = s.second.size() + 1U;
5010
0
                    pair->filePathName = new char[siz];
5011
0
#ifndef _MSC_VER
5012
0
                    strncpy(pair->filePathName, s.second.c_str(), siz);
5013
#else   // _MSC_VER
5014
                    strncpy_s(pair->filePathName, siz, s.second.c_str(), siz);
5015
#endif  // _MSC_VER
5016
0
                    pair->filePathName[siz - 1U] = '\0';
5017
0
                }
5018
0
            }
5019
5020
0
            return res;
5021
0
        }
5022
0
    }
5023
5024
0
    return res;
5025
0
}
5026
5027
0
IGFD_C_API char* IGFD_GetFilePathName(ImGuiFileDialog* vContextPtr, IGFD_ResultMode vMode) {
5028
0
    char* res = nullptr;
5029
5030
0
    if (vContextPtr != nullptr) {
5031
0
        auto s = vContextPtr->GetFilePathName(vMode);
5032
0
        if (!s.empty()) {
5033
0
            size_t siz = s.size() + 1U;
5034
0
            res        = (char*)malloc(siz);
5035
0
            if (res) {
5036
0
#ifndef _MSC_VER
5037
0
                strncpy(res, s.c_str(), siz);
5038
#else   // _MSC_VER
5039
                strncpy_s(res, siz, s.c_str(), siz);
5040
#endif  // _MSC_VER
5041
0
                res[siz - 1U] = '\0';
5042
0
            }
5043
0
        }
5044
0
    }
5045
5046
0
    return res;
5047
0
}
5048
5049
0
IGFD_C_API char* IGFD_GetCurrentFileName(ImGuiFileDialog* vContextPtr, IGFD_ResultMode vMode) {
5050
0
    char* res = nullptr;
5051
5052
0
    if (vContextPtr != nullptr) {
5053
0
        auto s = vContextPtr->GetCurrentFileName(vMode);
5054
0
        if (!s.empty()) {
5055
0
            size_t siz = s.size() + 1U;
5056
0
            res        = (char*)malloc(siz);
5057
0
            if (res) {
5058
0
#ifndef _MSC_VER
5059
0
                strncpy(res, s.c_str(), siz);
5060
#else   // _MSC_VER
5061
                strncpy_s(res, siz, s.c_str(), siz);
5062
#endif  // _MSC_VER
5063
0
                res[siz - 1U] = '\0';
5064
0
            }
5065
0
        }
5066
0
    }
5067
5068
0
    return res;
5069
0
}
5070
5071
0
IGFD_C_API char* IGFD_GetCurrentPath(ImGuiFileDialog* vContextPtr) {
5072
0
    char* res = nullptr;
5073
5074
0
    if (vContextPtr != nullptr) {
5075
0
        auto s = vContextPtr->GetCurrentPath();
5076
0
        if (!s.empty()) {
5077
0
            size_t siz = s.size() + 1U;
5078
0
            res        = (char*)malloc(siz);
5079
0
            if (res) {
5080
0
#ifndef _MSC_VER
5081
0
                strncpy(res, s.c_str(), siz);
5082
#else   // _MSC_VER
5083
                strncpy_s(res, siz, s.c_str(), siz);
5084
#endif  // _MSC_VER
5085
0
                res[siz - 1U] = '\0';
5086
0
            }
5087
0
        }
5088
0
    }
5089
5090
0
    return res;
5091
0
}
5092
5093
0
IGFD_C_API char* IGFD_GetCurrentFilter(ImGuiFileDialog* vContextPtr) {
5094
0
    char* res = nullptr;
5095
5096
0
    if (vContextPtr != nullptr) {
5097
0
        auto s = vContextPtr->GetCurrentFilter();
5098
0
        if (!s.empty()) {
5099
0
            size_t siz = s.size() + 1U;
5100
0
            res        = (char*)malloc(siz);
5101
0
            if (res) {
5102
0
#ifndef _MSC_VER
5103
0
                strncpy(res, s.c_str(), siz);
5104
#else   // _MSC_VER
5105
                strncpy_s(res, siz, s.c_str(), siz);
5106
#endif  // _MSC_VER
5107
0
                res[siz - 1U] = '\0';
5108
0
            }
5109
0
        }
5110
0
    }
5111
5112
0
    return res;
5113
0
}
5114
5115
0
IGFD_C_API void* IGFD_GetUserDatas(ImGuiFileDialog* vContextPtr) {
5116
0
    if (vContextPtr != nullptr) {
5117
0
        return vContextPtr->GetUserDatas();
5118
0
    }
5119
5120
0
    return nullptr;
5121
0
}
5122
5123
IGFD_C_API void IGFD_SetFileStyle(ImGuiFileDialog* vContextPtr, IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4 vColor, const char* vIcon,
5124
                                  ImFont* vFont)  //-V813
5125
0
{
5126
0
    if (vContextPtr != nullptr) {
5127
0
        vContextPtr->SetFileStyle(vFlags, vCriteria, vColor, vIcon, vFont);
5128
0
    }
5129
0
}
5130
5131
0
IGFD_C_API void IGFD_SetFileStyle2(ImGuiFileDialog* vContextPtr, IGFD_FileStyleFlags vFlags, const char* vCriteria, float vR, float vG, float vB, float vA, const char* vIcon, ImFont* vFont) {
5132
0
    if (vContextPtr != nullptr) {
5133
0
        vContextPtr->SetFileStyle(vFlags, vCriteria, ImVec4(vR, vG, vB, vA), vIcon, vFont);
5134
0
    }
5135
0
}
5136
5137
0
IGFD_C_API bool IGFD_GetFileStyle(ImGuiFileDialog* vContextPtr, IGFD_FileStyleFlags vFlags, const char* vCriteria, ImVec4* vOutColor, char** vOutIconText, ImFont** vOutFont) {
5138
0
    if (vContextPtr != nullptr) {
5139
0
        std::string icon;
5140
0
        bool res = vContextPtr->GetFileStyle(vFlags, vCriteria, vOutColor, &icon, vOutFont);
5141
0
        if (!icon.empty() && vOutIconText) {
5142
0
            size_t siz    = icon.size() + 1U;
5143
0
            *vOutIconText = (char*)malloc(siz);
5144
0
            if (*vOutIconText) {
5145
0
#ifndef _MSC_VER
5146
0
                strncpy(*vOutIconText, icon.c_str(), siz);
5147
#else   // _MSC_VER
5148
                strncpy_s(*vOutIconText, siz, icon.c_str(), siz);
5149
#endif  // _MSC_VER
5150
0
                (*vOutIconText)[siz - 1U] = '\0';
5151
0
            }
5152
0
        }
5153
0
        return res;
5154
0
    }
5155
5156
0
    return false;
5157
0
}
5158
5159
0
IGFD_C_API void IGFD_ClearFilesStyle(ImGuiFileDialog* vContextPtr) {
5160
0
    if (vContextPtr != nullptr) {
5161
0
        vContextPtr->ClearFilesStyle();
5162
0
    }
5163
0
}
5164
5165
0
IGFD_C_API void SetLocales(ImGuiFileDialog* vContextPtr, const int vCategory, const char* vBeginLocale, const char* vEndLocale) {
5166
0
    if (vContextPtr != nullptr) {
5167
0
        vContextPtr->SetLocales(vCategory, (vBeginLocale ? vBeginLocale : ""), (vEndLocale ? vEndLocale : ""));
5168
0
    }
5169
0
}
5170
5171
#ifdef USE_EXPLORATION_BY_KEYS
5172
IGFD_C_API void IGFD_SetFlashingAttenuationInSeconds(ImGuiFileDialog* vContextPtr, float vAttenValue) {
5173
    if (vContextPtr != nullptr) {
5174
        vContextPtr->SetFlashingAttenuationInSeconds(vAttenValue);
5175
    }
5176
}
5177
#endif
5178
5179
#ifdef USE_PLACES_FEATURE
5180
IGFD_C_API char* IGFD_SerializePlaces(ImGuiFileDialog* vContextPtr, bool vDontSerializeCodeBasedPlaces) {
5181
    char* res = nullptr;
5182
5183
    if (vContextPtr != nullptr) {
5184
        auto s = vContextPtr->SerializePlaces(vDontSerializeCodeBasedPlaces);
5185
        if (!s.empty()) {
5186
            size_t siz = s.size() + 1U;
5187
            res        = (char*)malloc(siz);
5188
            if (res) {
5189
#ifndef _MSC_VER
5190
                strncpy(res, s.c_str(), siz);
5191
#else   // _MSC_VER
5192
                strncpy_s(res, siz, s.c_str(), siz);
5193
#endif  // _MSC_VER
5194
                res[siz - 1U] = '\0';
5195
            }
5196
        }
5197
    }
5198
5199
    return res;
5200
}
5201
5202
IGFD_C_API void IGFD_DeserializePlaces(ImGuiFileDialog* vContextPtr, const char* vPlaces) {
5203
    if (vContextPtr != nullptr) {
5204
        vContextPtr->DeserializePlaces(vPlaces);
5205
    }
5206
}
5207
5208
IGFD_C_API bool IGFD_AddPlacesGroup(ImGuiFileDialog* vContextPtr, const char* vGroupName, size_t vDisplayOrder, bool vCanBeEdited) {
5209
    if (vContextPtr != nullptr) {
5210
        return vContextPtr->AddPlacesGroup(vGroupName, vDisplayOrder, vCanBeEdited);
5211
    }
5212
    return false;
5213
}
5214
5215
IGFD_C_API bool IGFD_RemovePlacesGroup(ImGuiFileDialog* vContextPtr, const char* vGroupName) {
5216
    if (vContextPtr != nullptr) {
5217
        return vContextPtr->RemovePlacesGroup(vGroupName);
5218
    }
5219
    return false;
5220
}
5221
5222
IGFD_C_API bool IGFD_AddPlace(ImGuiFileDialog* vContextPtr, const char* vGroupName, const char* vPlaceName, const char* vPlacePath, bool vCanBeSaved, const char* vIconText) {
5223
    if (vContextPtr != nullptr) {
5224
        auto group_ptr = vContextPtr->GetPlacesGroupPtr(vGroupName);
5225
        if (group_ptr != nullptr) {
5226
            IGFD::FileStyle style;
5227
            style.icon = vIconText;
5228
            return group_ptr->AddPlace(vPlaceName, vPlacePath, vCanBeSaved, style);
5229
        }
5230
    }
5231
    return false;
5232
}
5233
5234
IGFD_C_API bool IGFD_RemovePlace(ImGuiFileDialog* vContextPtr, const char* vGroupName, const char* vPlaceName) {
5235
    if (vContextPtr != nullptr) {
5236
        auto group_ptr = vContextPtr->GetPlacesGroupPtr(vGroupName);
5237
        if (group_ptr != nullptr) {
5238
            return group_ptr->RemovePlace(vPlaceName);
5239
        }
5240
    }
5241
    return false;
5242
}
5243
5244
#endif
5245
5246
#ifdef USE_THUMBNAILS
5247
IGFD_C_API void SetCreateThumbnailCallback(ImGuiFileDialog* vContextPtr, const IGFD_CreateThumbnailFun vCreateThumbnailFun) {
5248
    if (vContextPtr != nullptr) {
5249
        vContextPtr->SetCreateThumbnailCallback(vCreateThumbnailFun);
5250
    }
5251
}
5252
5253
IGFD_C_API void SetDestroyThumbnailCallback(ImGuiFileDialog* vContextPtr, const IGFD_DestroyThumbnailFun vDestroyThumbnailFun) {
5254
    if (vContextPtr != nullptr) {
5255
        vContextPtr->SetDestroyThumbnailCallback(vDestroyThumbnailFun);
5256
    }
5257
}
5258
5259
IGFD_C_API void ManageGPUThumbnails(ImGuiFileDialog* vContextPtr) {
5260
    if (vContextPtr != nullptr) {
5261
        vContextPtr->ManageGPUThumbnails();
5262
    }
5263
}
5264
#endif  // USE_THUMBNAILS
5265
5266
#pragma endregion
/Users/kiltum/projects/zxcpp/lib/imguifiledialog/ImGuiFileDialog.h
Line
Count
Source
1
/*
2
  _____              _____         _  ______  _  _        _____   _         _
3
 |_   _|            / ____|       (_)|  ____|(_)| |      |  __ \ (_)       | |
4
   | |   _ __ ___  | |  __  _   _  _ | |__    _ | |  ___ | |  | | _   __ _ | |  ___    __ _
5
   | |  | '_ ` _ \ | | |_ || | | || ||  __|  | || | / _ \| |  | || | / _` || | / _ \  / _` |
6
  _| |_ | | | | | || |__| || |_| || || |     | || ||  __/| |__| || || (_| || || (_) || (_| |
7
 |_____||_| |_| |_| \_____| \__,_||_||_|     |_||_| \___||_____/ |_| \__,_||_| \___/  \__, |
8
                                                                                       __/ |
9
                                                                                      |___/
10
                                      ___      __     ___
11
                                     / _ \    / /    / _ \
12
                             __   __| | | |  / /_   | (_) |
13
                             \ \ / /| | | | | '_ \   > _ <
14
                              \ V / | |_| |_| (_) |_| (_) |
15
                               \_/   \___/(_)\___/(_)\___/
16
17
GITHUB REPOT : https://github.com/aiekick/ImGuiFileDialog
18
DOCUMENTATION : see the attached Documentation.md
19
20
generated with "Text to ASCII Art Generator (TAAG)"
21
https://patorjk.com/software/taag/#p=display&h=1&v=0&f=Big&t=ImGuiFileDialog%0Av0.6.8
22
23
MIT License
24
25
Copyright (c) 2018-2025 Stephane Cuillerdier (aka aiekick)
26
27
Permission is hereby granted, free of charge, to any person obtaining a copy
28
of this software and associated documentation files (the "Software"), to deal
29
in the Software without restriction, including without limitation the rights
30
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31
copies of the Software, and to permit persons to whom the Software is
32
furnished to do so, subject to the following conditions:
33
34
The above copyright notice and this permission notice shall be included in all
35
copies or substantial portions of the Software.
36
37
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
SOFTWARE.
44
*/
45
46
#pragma once
47
48
#define IGFD_VERSION "v0.6.8"
49
#define IGFD_IMGUI_SUPPORTED_VERSION "1.92.3"
50
51
// Config file
52
#ifndef CUSTOM_IMGUIFILEDIALOG_CONFIG
53
#include "ImGuiFileDialogConfig.h"
54
#else  // CUSTOM_IMGUIFILEDIALOG_CONFIG
55
#include CUSTOM_IMGUIFILEDIALOG_CONFIG
56
#endif  // CUSTOM_IMGUIFILEDIALOG_CONFIG
57
58
// Define attributes of all API symbols declarations (e.g. for DLL under Windows)
59
// Using ImGuiFileDialog via a shared library is not recommended, because we don't guarantee
60
// backward nor forward ABI compatibility and also function call overhead. If you
61
// do use ImGuiFileDialog as a DLL, be sure to call ImGui::SetImGuiContext (see ImGui doc Miscellanous section).
62
63
#ifndef IGFD_API
64
#define IGFD_API
65
#endif  // IGFD_API
66
67
///////////////////////////////////////////////////////////
68
/////////////// FLAGS /////////////////////////////////////
69
///////////////////////////////////////////////////////////
70
71
// file style enum for file display (color, icon, font)
72
typedef int IGFD_FileStyleFlags;  // -> enum IGFD_FileStyleFlags_
73
enum IGFD_FileStyleFlags_         // by evaluation / priority order
74
{
75
    IGFD_FileStyle_None                 = 0,         // define none style
76
    IGFD_FileStyleByTypeFile            = (1 << 0),  // define style for all files
77
    IGFD_FileStyleByTypeDir             = (1 << 1),  // define style for all dir
78
    IGFD_FileStyleByTypeLink            = (1 << 2),  // define style for all link
79
    IGFD_FileStyleByExtention           = (1 << 3),  // define style by extention, for files or links
80
    IGFD_FileStyleByFullName            = (1 << 4),  // define style for particular file/dir/link full name (filename + extention)
81
    IGFD_FileStyleByContainedInFullName = (1 << 5),  // define style for file/dir/link when criteria is contained in full name
82
};
83
84
typedef int ImGuiFileDialogFlags;  // -> enum ImGuiFileDialogFlags_
85
enum ImGuiFileDialogFlags_ {
86
    ImGuiFileDialogFlags_None = 0,                                      // define none default flag
87
    ImGuiFileDialogFlags_ConfirmOverwrite = (1 << 0),                   // show confirm to overwrite dialog
88
    ImGuiFileDialogFlags_DontShowHiddenFiles = (1 << 1),                // dont show hidden file (file starting with a .)
89
    ImGuiFileDialogFlags_DisableCreateDirectoryButton = (1 << 2),       // disable the create directory button
90
    ImGuiFileDialogFlags_HideColumnType = (1 << 3),                     // hide column file type
91
    ImGuiFileDialogFlags_HideColumnSize = (1 << 4),                     // hide column file size
92
    ImGuiFileDialogFlags_HideColumnDate = (1 << 5),                     // hide column file date
93
    ImGuiFileDialogFlags_NoDialog = (1 << 6),                           // let the dialog embedded in your own imgui begin / end scope
94
    ImGuiFileDialogFlags_ReadOnlyFileNameField = (1 << 7),              // don't let user type in filename field for file open style dialogs
95
    ImGuiFileDialogFlags_CaseInsensitiveExtentionFiltering = (1 << 8),  // the file extentions filtering will not take into account the case
96
    ImGuiFileDialogFlags_Modal = (1 << 9),                              // modal
97
    ImGuiFileDialogFlags_DisableThumbnailMode = (1 << 10),              // disable the thumbnail mode
98
    ImGuiFileDialogFlags_DisablePlaceMode = (1 << 11),                  // disable the place mode
99
    ImGuiFileDialogFlags_DisableQuickPathSelection = (1 << 12),         // disable the quick path selection
100
    ImGuiFileDialogFlags_ShowDevicesButton = (1 << 13),                 // show the devices selection button
101
    ImGuiFileDialogFlags_NaturalSorting = (1 << 14),                    // enable the antural sorting for filenames and extentions, slower than standard sorting
102
    ImGuiFileDialogFlags_OptionalFileName = (1 << 15),                  // the input filename is optional, so the dialog can be validated even if the filebname input is empty
103
104
    // default behavior when no flags is defined. seems to be the more common cases
105
    ImGuiFileDialogFlags_Default = ImGuiFileDialogFlags_ConfirmOverwrite |  //
106
        ImGuiFileDialogFlags_Modal |                                        //
107
        ImGuiFileDialogFlags_HideColumnType
108
};
109
110
// flags used for GetFilePathName(flag) or GetSelection(flag)
111
typedef int IGFD_ResultMode;  // -> enum IGFD_ResultMode_
112
enum IGFD_ResultMode_ {
113
    // IGFD_ResultMode_AddIfNoFileExt
114
    // add the file ext only if there is no file ext
115
    //   filter {.cpp,.h} with file :
116
    //     toto.h => toto.h
117
    //     toto.a.h => toto.a.h
118
    //     toto.a. => toto.a.cpp
119
    //     toto. => toto.cpp
120
    //     toto => toto.cpp
121
    //   filter {.z,.a.b} with file :
122
    //     toto.a.h => toto.a.h
123
    //     toto. => toto.z
124
    //     toto => toto.z
125
    //   filter {.g.z,.a} with file :
126
    //     toto.a.h => toto.a.h
127
    //     toto. => toto.g.z
128
    //     toto => toto.g.z
129
    IGFD_ResultMode_AddIfNoFileExt = 0,  // default
130
131
    // IGFD_ResultMode_OverwriteFileExt
132
    // Overwrite the file extention by the current filter :
133
    //   filter {.cpp,.h} with file :
134
    //     toto.h => toto.cpp
135
    //     toto.a.h => toto.a.cpp
136
    //     toto.a. => toto.a.cpp
137
    //     toto.a.h.t => toto.a.h.cpp
138
    //     toto. => toto.cpp
139
    //     toto => toto.cpp
140
    //   filter {.z,.a.b} with file :
141
    //     toto.a.h => toto.z
142
    //     toto.a.h.t => toto.a.z
143
    //     toto. => toto.z
144
    //     toto => toto.z
145
    //   filter {.g.z,.a} with file :
146
    //     toto.a.h => toto.g.z
147
    //     toto.a.h.y => toto.a.g.z
148
    //     toto.a. => toto.g.z
149
    //     toto. => toto.g.z
150
    //     toto => toto.g.z
151
    IGFD_ResultMode_OverwriteFileExt = 1,  // behavior pre IGFD v0.6.6
152
153
    // IGFD_ResultMode_KeepInputFile
154
    // keep the input file => no modification :
155
    //   filter {.cpp,.h} with file :
156
    //      toto.h => toto.h
157
    //      toto. => toto.
158
    //      toto => toto
159
    //   filter {.z,.a.b} with file :
160
    //      toto.a.h => toto.a.h
161
    //      toto. => toto.
162
    //      toto => toto
163
    //   filter {.g.z,.a} with file :
164
    //      toto.a.h => toto.a.h
165
    //      toto. => toto.
166
    //      toto => toto
167
    IGFD_ResultMode_KeepInputFile = 2
168
};
169
170
///////////////////////////////////////////////////////////
171
/////////////// STRUCTS ///////////////////////////////////
172
///////////////////////////////////////////////////////////
173
174
#ifdef USE_THUMBNAILS
175
struct IGFD_Thumbnail_Info {
176
    int isReadyToDisplay            = 0;  // ready to be rendered, so texture created
177
    int isReadyToUpload             = 0;  // ready to upload to gpu
178
    int isLoadingOrLoaded           = 0;  // was sent to laoding or loaded
179
    int textureWidth                = 0;  // width of the texture to upload
180
    int textureHeight               = 0;  // height of the texture to upload
181
    int textureChannels             = 0;  // count channels of the texture to upload
182
    unsigned char* textureFileDatas = 0;  // file texture datas, will be rested to null after gpu upload
183
    void* textureID                 = 0;  // 2d texture id (void* is like ImtextureID type) (GL, DX, VK, Etc..)
184
    void* userDatas                 = 0;  // user datas
185
};
186
#endif  // USE_THUMBNAILS
187
188
// stdint is used for cpp and c apî (cstdint is only for cpp)
189
#include <stdint.h>
190
191
#ifdef __cplusplus
192
193
#ifndef IMGUI_DEFINE_MATH_OPERATORS
194
#define IMGUI_DEFINE_MATH_OPERATORS
195
#endif  // IMGUI_DEFINE_MATH_OPERATORS
196
197
#ifdef IMGUI_INCLUDE
198
#include IMGUI_INCLUDE
199
#else  // IMGUI_INCLUDE
200
#include <imgui.h>
201
#endif  // IMGUI_INCLUDE
202
203
#include <set>
204
#include <map>
205
#include <list>
206
#include <regex>
207
#include <array>
208
#include <mutex>
209
#include <condition_variable>
210
#include <thread>
211
#include <cfloat>
212
#include <memory>
213
#include <string>
214
#include <vector>
215
#include <utility>
216
#include <fstream>
217
#include <functional>
218
#include <unordered_map>
219
220
#ifndef defaultSortField
221
#define defaultSortField FIELD_FILENAME
222
#endif  // defaultSortField
223
224
#ifndef defaultSortOrderFilename
225
0
#define defaultSortOrderFilename true
226
#endif  // defaultSortOrderFilename
227
#ifndef defaultSortOrderType
228
0
#define defaultSortOrderType true
229
#endif  // defaultSortOrderType
230
#ifndef defaultSortOrderSize
231
0
#define defaultSortOrderSize true
232
#endif  // defaultSortOrderSize
233
#ifndef defaultSortOrderDate
234
0
#define defaultSortOrderDate true
235
#endif  // defaultSortOrderDate
236
#ifndef defaultSortOrderThumbnails
237
#define defaultSortOrderThumbnails true
238
#endif  // defaultSortOrderThumbnails
239
240
#ifndef MAX_FILE_DIALOG_NAME_BUFFER
241
0
#define MAX_FILE_DIALOG_NAME_BUFFER 1024
242
#endif  // MAX_FILE_DIALOG_NAME_BUFFER
243
244
#ifndef MAX_PATH_BUFFER_SIZE
245
0
#define MAX_PATH_BUFFER_SIZE 1024
246
#endif  // MAX_PATH_BUFFER_SIZE
247
248
#ifndef EXT_MAX_LEVEL
249
#define EXT_MAX_LEVEL 10U
250
#endif  // EXT_MAX_LEVEL
251
252
namespace IGFD {
253
254
template <typename T>
255
class SearchableVector {
256
private:
257
    std::unordered_map<T, size_t> m_Dico;
258
    std::vector<T> m_Array;
259
260
public:
261
0
    void clear() {
262
0
        m_Dico.clear();
263
0
        m_Array.clear();
264
0
    }
265
0
    bool empty() const {
266
0
        return m_Array.empty();
267
0
    }
268
    size_t size() const {
269
        return m_Array.size();
270
    }
271
    T& operator[](const size_t& vIdx) {
272
        return m_Array[vIdx];
273
    }
274
    T& at(const size_t& vIdx) {
275
        return m_Array.at(vIdx);
276
    }
277
    typename std::vector<T>::iterator begin() {
278
        return m_Array.begin();
279
    }
280
0
    typename std::vector<T>::const_iterator begin() const {
281
0
        return m_Array.begin();
282
0
    }
283
    typename std::vector<T>::iterator end() {
284
        return m_Array.end();
285
    }
286
0
    typename std::vector<T>::const_iterator end() const {
287
0
        return m_Array.end();
288
0
    }
289
290
0
    bool try_add(T vKey) {
291
0
        if (!exist(vKey)) {
292
0
            m_Dico[vKey] = m_Array.size();
293
0
            m_Array.push_back(vKey);
294
0
            return true;
295
0
        }
296
0
        return false;
297
0
    }
298
299
    bool try_set_existing(T vKey) {
300
        if (exist(vKey)) {
301
            auto row     = m_Dico.at(vKey);
302
            m_Array[row] = vKey;
303
            return true;
304
        }
305
        return false;
306
    }
307
308
0
    bool exist(const std::string& vKey) const {
309
0
        return (m_Dico.find(vKey) != m_Dico.end());
310
0
    }
311
};
312
313
class IGFD_API FileInfos;
314
class IGFD_API FileDialogInternal;
315
316
class IGFD_API Utils {
317
    friend class TestUtils;
318
319
public:
320
    struct PathStruct {
321
        std::string path;
322
        std::string name;
323
        std::string ext;
324
        bool isOk = false;
325
    };
326
327
public:
328
    static bool ImSplitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f);
329
    static bool ReplaceString(std::string& str, const std::string& oldStr, const std::string& newStr, const size_t& vMaxRecursion = 10U);
330
    static void AppendToBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr);
331
    static void ResetBuffer(char* vBuffer);
332
    static void SetBuffer(char* vBuffer, size_t vBufferLen, const std::string& vStr);
333
    static std::string UTF8Encode(const std::wstring& wstr);
334
    static std::wstring UTF8Decode(const std::string& str);
335
    static std::vector<std::string> SplitStringToVector(const std::string& vText, const std::string& vDelimiterPattern, const bool vPushEmpty);
336
    static std::vector<std::string> SplitStringToVector(const std::string& vText, const char& vDelimiter, const bool vPushEmpty);
337
    static std::string LowerCaseString(const std::string& vString);  // turn all text in lower case for search facilitie
338
    static size_t GetCharCountInString(const std::string& vString, const char& vChar);
339
    static size_t GetLastCharPosWithMinCharCount(const std::string& vString, const char& vChar, const size_t& vMinCharCount);
340
    static std::string GetPathSeparator();                                                                              // return the slash for any OS ( \\ win, / unix)
341
    static std::string RoundNumber(double vvalue, int n);                                                               // custom rounding number
342
    static std::pair<std::string, std::string> FormatFileSize(size_t vByteSize);                                        // format file size field. return pair(number, unit)
343
    static bool NaturalCompare(const std::string& vA, const std::string& vB, bool vInsensitiveCase, bool vDescending);  // natural sorting
344
345
private:
346
    static bool M_IsAValidCharExt(const char& c);
347
    static bool M_IsAValidCharSuffix(const char& c);
348
    static bool M_ExtractNumFromStringAtPos(const std::string& str, size_t& pos, double& vOutNum);
349
};
350
351
class IGFD_API FileStyle {
352
public:
353
    typedef std::function<bool(const FileInfos&, FileStyle&)> FileStyleFunctor;
354
355
public:
356
    ImVec4 color = ImVec4(0, 0, 0, 0);
357
    std::string icon;
358
    ImFont* font              = nullptr;
359
    IGFD_FileStyleFlags flags = 0;
360
361
public:
362
    FileStyle();
363
    FileStyle(const FileStyle& vStyle);
364
    FileStyle(const ImVec4& vColor, const std::string& vIcon = "", ImFont* vFont = nullptr);
365
};
366
367
class IGFD_API SearchManager {
368
public:
369
    std::string searchTag;
370
    char searchBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = "";
371
    bool searchInputIsActive                       = false;
372
373
public:
374
    void Clear();                                                 // clear datas
375
    void DrawSearchBar(FileDialogInternal& vFileDialogInternal);  // draw the search bar
376
};
377
378
class IGFD_API FilterInfos {
379
private:
380
    // just for return a default const std::string& in getFirstFilter.
381
    // cannot be const, because FilterInfos must be affected to an another FilterInfos
382
    // must stay empty all time
383
    std::string empty_string;
384
385
public:
386
    std::string title;                                // displayed filter.can be different than rela filter
387
    SearchableVector<std::string> filters;            // filters
388
    SearchableVector<std::string> filters_optimized;  // optimized filters for case insensitive search
389
    std::vector<std::regex> filters_regex;            // collection of regex filter type
390
    size_t count_dots = 0U;                           // the max count dot the max per filter of all filters
391
392
public:
393
    void clear();                                                                        // clear the datas
394
    bool empty() const;                                                                  // is filter empty
395
    const std::string& getFirstFilter() const;                                           // get the first filter
396
    bool regexExist(const std::string& vFilter) const;                                   // is regex filter exist
397
    bool exist(const FileInfos& vFileInfos, bool vIsCaseInsensitive) const;              // is filter exist
398
    void setCollectionTitle(const std::string& vTitle);                                  // set the collection title
399
    void addFilter(const std::string& vFilter, const bool vIsRegex);                    // add a filter
400
    void addCollectionFilter(const std::string& vFilter, const bool vIsRegex);          // add a filter in collection
401
    static std::string transformAsteriskBasedFilterToRegex(const std::string& vFilter);  // will transform a filter who contain * to a regex
402
};
403
404
class IGFD_API FilterManager {
405
    friend class TestFilterManager;
406
private:
407
    std::vector<FilterInfos> m_ParsedFilters;
408
    std::unordered_map<IGFD_FileStyleFlags, std::unordered_map<std::string, std::shared_ptr<FileStyle> > > m_FilesStyle;  // file infos for file
409
                                                                                                                          // extention only
410
    std::vector<FileStyle::FileStyleFunctor> m_FilesStyleFunctors;                                                        // file style via lambda function
411
    FilterInfos m_SelectedFilter;
412
413
public:
414
    std::string dLGFilters;
415
    std::string dLGdefaultExt;
416
417
public:
418
    const FilterInfos& GetSelectedFilter() const;
419
    void ParseFilters(const char* vFilters);                                                               // Parse filter syntax, detect and parse filter collection
420
    void SetSelectedFilterWithExt(const std::string& vFilter);                                             // Select filter
421
    bool FillFileStyle(std::shared_ptr<FileInfos> vFileInfos) const;                                       // fill with the good style
422
    void SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const FileStyle& vInfos);  // Set FileStyle
423
    void SetFileStyle(const IGFD_FileStyleFlags& vFlags, const char* vCriteria, const ImVec4& vColor, const std::string& vIcon,
424
                      ImFont* vFont);                         // link file style to Color and Icon and Font
425
    void SetFileStyle(FileStyle::FileStyleFunctor vFunctor);  // lambda functor for set file style.
426
    bool GetFileStyle(const IGFD_FileStyleFlags& vFlags, const std::string& vCriteria, ImVec4* vOutColor, std::string* vOutIcon,
427
                      ImFont** vOutFont);  // Get Color and Icon for Filter
428
    void ClearFilesStyle();                // clear m_FileStyle
429
    bool IsCoveredByFilters(const FileInfos& vFileInfos,
430
                            bool vIsCaseInsensitive) const;            // check if current file extention (vExt) is covered by current filter, or by regex (vNameExt)
431
    float GetFilterComboBoxWidth() const;                              // will return the current combo box widget width
432
    bool DrawFilterComboBox(FileDialogInternal& vFileDialogInternal);  // draw the filter combobox  // get the current selected filter
433
    std::string ReplaceExtentionWithCurrentFilterIfNeeded(const std::string& vFileName,
434
                                                          IGFD_ResultMode vFlag) const;  // replace the extention of the current file by the selected filter
435
    void SetDefaultFilterIfNotDefined();                                                 // define the first filter if no filter is selected
436
};
437
438
class IGFD_API FileType {
439
public:
440
    enum class ContentType {
441
        // The ordering will be used during sort.
442
        Invalid       = -1,
443
        Directory     = 0,
444
        File          = 1,
445
        LinkToUnknown = 2,  // link to something that is not a regular file or directory.
446
    };
447
448
private:
449
    ContentType m_Content = ContentType::Invalid;
450
    bool m_Symlink        = false;
451
452
public:
453
    FileType();
454
    FileType(const ContentType& vContentType, const bool vIsSymlink);
455
456
    void SetContent(const ContentType& vContentType);
457
    void SetSymLink(const bool vIsSymlink);
458
459
    bool isValid() const;
460
    bool isDir() const;
461
    bool isFile() const;
462
    bool isLinkToUnknown() const;
463
    bool isSymLink() const;
464
465
    // Comparisons only care about the content type, ignoring whether it's a symlink or not.
466
    bool operator==(const FileType& rhs) const;
467
    bool operator!=(const FileType& rhs) const;
468
    bool operator<(const FileType& rhs) const;
469
    bool operator>(const FileType& rhs) const;
470
};
471
472
class IGFD_API FileInfos {
473
public:
474
    static std::shared_ptr<FileInfos> create();
475
476
public:
477
    // extention of the file, the array is the levels of ext, by ex : .a.b.c, will be save in {.a.b.c, .b.c, .c}
478
    // 10 level max are sufficient i guess. the others levels will be checked if countExtDot > 1
479
    std::array<std::string, EXT_MAX_LEVEL> fileExtLevels;
480
    std::array<std::string, EXT_MAX_LEVEL> fileExtLevels_optimized;  // optimized for search => insensitivecase
481
    // same for file name, can be sued in userFileAttributesFun
482
    std::array<std::string, EXT_MAX_LEVEL> fileNameLevels;
483
    std::array<std::string, EXT_MAX_LEVEL> fileNameLevels_optimized;  // optimized for search => insensitivecase
484
    size_t countExtDot = 0U;                                          // count dots in file extention. this count will give the levels in fileExtLevels
485
    FileType fileType;                                                // fileType
486
    std::string filePath;                                             // path of the file
487
    std::string fileName;                                             // file name only
488
    std::string fileNameExt;                                          // filename of the file (file name + extention) (but no path)
489
    std::string fileNameExt_optimized;                                // optimized for search => insensitivecase
490
    std::string deviceInfos;                                          // quick infos to display after name for devices
491
    std::string tooltipMessage;                                       // message to display on the tooltip, is not empty
492
    int32_t tooltipColumn = -1;                                       // the tooltip will appears only when the mouse is over the tooltipColumn if > -1
493
    size_t fileSize       = 0U;                                       // for sorting operations
494
    std::pair<std::string, std::string> formatedFileSize;             // file size formated (10 o, 10 ko, 10 mo, 10 go)
495
    std::string fileModifDate;                                        // file user defined format of the date (data + time by default)
496
    std::shared_ptr<FileStyle> fileStyle = nullptr;                   // style of the file
497
#ifdef USE_THUMBNAILS
498
    IGFD_Thumbnail_Info thumbnailInfo;  // structre for the display for image file tetxure
499
#endif                                  // USE_THUMBNAILS
500
501
public:
502
    bool SearchForTag(const std::string& vTag) const;  // will search a tag in fileNameExt and fileNameExt_optimized
503
    bool SearchForExt(const std::string& vExt, const bool vIsCaseInsensitive,
504
                      const size_t& vMaxLevel = EXT_MAX_LEVEL) const;  // will check the fileExtLevels levels for vExt, until vMaxLevel
505
    bool SearchForExts(const std::string& vComaSepExts, const bool vIsCaseInsensitive,
506
                       const size_t& vMaxLevel = EXT_MAX_LEVEL) const;  // will check the fileExtLevels levels for vExts (ext are coma separated), until vMaxLevel
507
    bool FinalizeFileTypeParsing(const size_t& vMaxDotToExtract);       // finalize the parsing the file (only a file or link to file. no dir)
508
};
509
510
typedef std::pair<std::string, std::string> PathDisplayedName;
511
512
class IFileSystem {
513
public:
514
1
    virtual ~IFileSystem() = default;
515
    // say if a directory can be openened or for any reason locked
516
    virtual bool IsDirectoryCanBeOpened(const std::string& vName) = 0;
517
    // say if a directory exist
518
    virtual bool IsDirectoryExist(const std::string& vName) = 0;
519
    // say if a file exist
520
    virtual bool IsFileExist(const std::string& vName) = 0;
521
    // say if a directory was created, return false if vName is invalid or alreayd exist
522
    virtual bool CreateDirectoryIfNotExist(const std::string& vName) = 0;
523
    // extract the component of a file path name, like path, name, ext
524
    virtual IGFD::Utils::PathStruct ParsePathFileName(const std::string& vPathFileName) = 0;
525
    // will return a list of files inside a path
526
    virtual std::vector<IGFD::FileInfos> ScanDirectory(const std::string& vPath) = 0;
527
    // say if the path is well a directory
528
    virtual bool IsDirectory(const std::string& vFilePathName) = 0;
529
    // return a device list (<path, device name>) on windows, but can be used on other platforms for give to the user a list of devices paths.
530
    virtual std::vector<IGFD::PathDisplayedName> GetDevicesList() = 0;
531
    // return via argument the date and the size of a file (for solve issue regarding apis and widechars)
532
    virtual void GetFileDateAndSize(const std::string& vFilePathName, const IGFD::FileType& vFileType, std::string& voDate, size_t& voSize) = 0;
533
};
534
535
class IGFD_API FileManager {
536
    friend class TestFileManager;
537
public:                            // types
538
    enum class SortingFieldEnum {  // sorting for filetering of the file lsit
539
        FIELD_NONE = 0,            // no sorting reference, result indetermined haha..
540
        FIELD_FILENAME,            // sorted by filename
541
        FIELD_TYPE,                // sorted by filetype
542
        FIELD_SIZE,                // sorted by filesize (formated file size)
543
        FIELD_DATE,                // sorted by filedate
544
        FIELD_THUMBNAILS,          // sorted by thumbnails (comparaison by width then by height)
545
    };
546
547
private:
548
    std::string m_CurrentPath;                                    // current path (to be decomposed in m_CurrentPathDecomposition
549
    std::vector<std::string> m_CurrentPathDecomposition;          // part words
550
    std::vector<std::shared_ptr<FileInfos> > m_FileList;          // base container
551
    std::vector<std::shared_ptr<FileInfos> > m_FilteredFileList;  // filtered container (search, sorting, etc..)
552
    std::vector<std::shared_ptr<FileInfos> > m_PathList;          // base container for path selection
553
    std::vector<std::shared_ptr<FileInfos> > m_FilteredPathList;  // filtered container for path selection (search, sorting, etc..)
554
    std::vector<std::string>::iterator m_PopupComposedPath;       // iterator on m_CurrentPathDecomposition for Current Path popup
555
    std::string m_LastSelectedFileName;                           // for shift multi selection
556
    std::set<std::string> m_SelectedFileNames;                    // the user selection of FilePathNames
557
    bool m_CreateDirectoryMode = false;                           // for create directory widget
558
    std::string m_FileSystemName;
559
    std::unique_ptr<IFileSystem> m_FileSystemPtr = nullptr;
560
561
public:
562
    bool inputPathActivated                               = false;  // show input for path edition
563
    bool devicesClicked                                   = false;  // event when a drive button is clicked
564
    bool pathClicked                                      = false;  // event when a path button was clicked
565
    char inputPathBuffer[MAX_PATH_BUFFER_SIZE]            = "";     // input path buffer for imgui widget input text (displayed in palce of composer)
566
    char variadicBuffer[MAX_FILE_DIALOG_NAME_BUFFER]      = "";     // called by m_SelectableItem
567
    char fileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER]      = "";     // file name buffer in footer for imgui widget input text
568
    char directoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = "";     // directory name buffer (when in directory mode)
569
    std::string headerFileName;                                     // detail view name of column file
570
    std::string headerFileType;                                     // detail view name of column type
571
    std::string headerFileSize;                                     // detail view name of column size
572
    std::string headerFileDate;                                     // detail view name of column date + time
573
#ifdef USE_THUMBNAILS
574
    std::string headerFileThumbnails;  // detail view name of column thumbnails
575
    bool sortingDirection[5] = {       // true => Ascending, false => Descending
576
        defaultSortOrderFilename, defaultSortOrderType, defaultSortOrderSize, defaultSortOrderDate, defaultSortOrderThumbnails};
577
#else
578
    bool sortingDirection[4] = {  // true => Ascending, false => Descending
579
        defaultSortOrderFilename, defaultSortOrderType, defaultSortOrderSize, defaultSortOrderDate};
580
#endif
581
    SortingFieldEnum sortingField = SortingFieldEnum::FIELD_FILENAME;  // detail view sorting column
582
    bool showDevices              = false;                             // devices are shown (only on os windows)
583
584
    std::string dLGpath;                  // base path set by user when OpenDialog was called
585
    std::string dLGDefaultFileName;       // base default file path name set by user when OpenDialog was called
586
    size_t dLGcountSelectionMax = 1U;     // 0 for infinite       // base max selection count set by user when OpenDialog was called
587
    bool dLGDirectoryMode       = false;  // is directory mode (defiend like : dLGDirectoryMode = (filters.empty()))
588
589
    std::string fsRoot;
590
591
private:
592
    void m_CompleteFileInfos(const std::shared_ptr<FileInfos>& vInfos);                    // set time and date infos of a file (detail view mode)
593
    void m_RemoveFileNameInSelection(const std::string& vFileName);                               // selection : remove a file name
594
    void m_AddFileNameInSelection(const std::string& vFileName, bool vSetLastSelectionFileName);  // selection : add a file name
595
    void m_AddFile(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName,
596
                   const FileType& vFileType);  // add file called by scandir
597
    void m_AddPath(const FileDialogInternal& vFileDialogInternal, const std::string& vPath, const std::string& vFileName,
598
                   const FileType& vFileType);  // add file called by scandir
599
    void m_ScanDirForPathSelection(const FileDialogInternal& vFileDialogInternal,
600
                                   const std::string& vPath);  // scan the directory for retrieve the path list
601
    void m_OpenPathPopup(const FileDialogInternal& vFileDialogInternal,
602
                         std::vector<std::string>::iterator vPathIter);   // open the popup list of paths
603
    void m_SetCurrentPath(std::vector<std::string>::iterator vPathIter);  // set the current path, update the path bar
604
    void m_ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal, std::vector<std::shared_ptr<FileInfos> >& vFileInfosList, std::vector<std::shared_ptr<FileInfos> >& vFileInfosFilteredList);
605
    static bool M_SortStrings(const FileDialogInternal& vFileDialogInternal,             //
606
                              const bool vInsensitiveCase, const bool vDescendingOrder,  //
607
                              const std::string& vA, const std::string& vB);
608
    void m_SortFields(const FileDialogInternal& vFileDialogInternal, std::vector<std::shared_ptr<FileInfos> >& vFileInfosList,
609
                      std::vector<std::shared_ptr<FileInfos> >& vFileInfosFilteredList);  // will sort a column
610
    bool m_CompleteFileInfosWithUserFileAttirbutes(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos);
611
612
public:
613
    FileManager();
614
    bool IsComposerEmpty() const;
615
    size_t GetComposerSize() const;
616
    bool IsFileListEmpty() const;
617
    bool IsPathListEmpty() const;
618
    bool IsFilteredListEmpty() const;
619
    bool IsPathFilteredListEmpty() const;
620
    size_t GetFullFileListSize() const;
621
    std::shared_ptr<FileInfos> GetFullFileAt(size_t vIdx);
622
    size_t GetFilteredListSize() const;
623
    size_t GetPathFilteredListSize() const;
624
    std::shared_ptr<FileInfos> GetFilteredFileAt(size_t vIdx);
625
    std::shared_ptr<FileInfos> GetFilteredPathAt(size_t vIdx);
626
    std::vector<std::string>::iterator GetCurrentPopupComposedPath() const;
627
    bool IsFileNameSelected(const std::string& vFileName);
628
    std::string GetBack();
629
    void ClearComposer();
630
    void ClearFileLists();  // clear file list, will destroy thumbnail textures
631
    void ClearPathLists();  // clear path list, will destroy thumbnail textures
632
    void ClearAll();
633
    void ApplyFilteringOnFileList(const FileDialogInternal& vFileDialogInternal);
634
    void SortFields(const FileDialogInternal& vFileDialogInternal);        // will sort a column
635
    void OpenCurrentPath(const FileDialogInternal& vFileDialogInternal);   // set the path of the dialog, will launch the scandir for populate the file listview
636
    bool GetDevices();                                                     // list devices
637
    bool CreateDir(const std::string& vPath);                              // create a directory on the file system
638
    std::string ComposeNewPath(std::vector<std::string>::iterator vIter);  // compose a path from the compose path widget
639
    bool SetPathOnParentDirectoryIfAny();                                  // compose paht on parent directory
640
    std::string GetCurrentPath();                                          // get the current path
641
    void SetCurrentPath(const std::string& vCurrentPath);                  // set the current path
642
    void SetDefaultFileName(const std::string& vFileName);
643
    bool SelectDirectory(const std::shared_ptr<FileInfos>& vInfos);  // enter directory
644
    void SelectAllFileNames();
645
    void SelectFileName(const std::shared_ptr<FileInfos>& vInfos);            // add a filename in selection
646
    void SelectOrDeselectFileName(const FileDialogInternal& vFileDialogInternal, const std::shared_ptr<FileInfos>& vInfos);  // add/remove a filename in selection
647
    void SetCurrentDir(const std::string& vPath);                                                                            // define current directory for scan
648
    void ScanDir(const FileDialogInternal& vFileDialogInternal,
649
                 const std::string& vPath);  // scan the directory for retrieve the file list
650
    std::string GetResultingPath();
651
    std::string GetResultingFileName(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag);
652
    std::string GetResultingFilePathName(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag);
653
    std::map<std::string, std::string> GetResultingSelection(FileDialogInternal& vFileDialogInternal, IGFD_ResultMode vFlag);
654
655
    void DrawDirectoryCreation(const FileDialogInternal& vFileDialogInternal);  // draw directory creation widget
656
    void DrawPathComposer(const FileDialogInternal& vFileDialogInternal);
657
658
0
    IFileSystem* GetFileSystemInstance() const {
659
0
        return m_FileSystemPtr.get();
660
0
    }
661
0
    const std::string& GetFileSystemName() const {
662
0
        return m_FileSystemName;
663
0
    }
664
};
665
666
typedef void* UserDatas;
667
typedef std::function<void(const char*, UserDatas, bool*)> PaneFun;        // side pane function binding
668
typedef std::function<bool(FileInfos*, UserDatas)> UserFileAttributesFun;  // custom file Attributes call back, reject file if false
669
670
struct IGFD_API FileDialogConfig {
671
    std::string path;                                        // path
672
    std::string fileName;                                    // defaut file name
673
    std::string filePathName;                                // if not empty, the filename and the path will be obtained from filePathName
674
    int32_t countSelectionMax  = 1;                          // count selection max, 0 for infinite
675
    UserDatas userDatas        = nullptr;                    // user datas (can be retrieved in pane)
676
    ImGuiFileDialogFlags flags = ImGuiFileDialogFlags_None;  // ImGuiFileDialogFlags
677
    PaneFun sidePane;                                        // side pane callback
678
    float sidePaneWidth = 250.0f;                            // side pane width
679
    UserFileAttributesFun userFileAttributes;                // user file Attibutes callback
680
};
681
682
class IGFD_API FileDialogInternal {
683
public:
684
    FileManager fileManager;      // the file manager
685
    FilterManager filterManager;  // the filter manager
686
    SearchManager searchManager;  // the search manager
687
688
public:
689
    std::string name;                          // the internal dialog name (title + ##word)
690
    bool showDialog           = false;         // the dialog is shown
691
    ImVec2 dialogCenterPos    = ImVec2(0, 0);  // center pos for display the confirm overwrite dialog
692
    int lastImGuiFrameCount   = 0;             // to be sure than only one dialog displayed per frame
693
    float footerHeight        = 0.0f;          // footer height
694
    bool canWeContinue        = true;          // events
695
    bool okResultToConfirm    = false;         // to confim if ok for OverWrite
696
    bool isOk                 = false;         // is dialog ok button click
697
    bool fileInputIsActive    = false;         // when input text for file or directory is active
698
    bool fileListViewIsActive = false;         // when list view is active
699
    std::string dLGkey;                        // the dialog key
700
    std::string dLGtitle;                      // the dialog title
701
    bool needToExitDialog  = false;            // we need to exit the dialog
702
    bool puUseCustomLocale = false;            // custom user locale
703
    int localeCategory     = LC_ALL;           // locale category to use
704
    std::string localeBegin;                   // the locale who will be applied at start of the display dialog
705
    std::string localeEnd;                     // the locale who will be applaied at end of the display dialog
706
707
private:
708
    FileDialogConfig m_DialogConfig;
709
710
public:
711
    void NewFrame();           // new frame, so maybe neded to do somethings, like reset events
712
    void EndFrame();           // end frame, so maybe neded to do somethings fater all
713
    void ResetForNewDialog();  // reset what is needed to reset for the openging of a new dialog
714
715
    void configureDialog(                  // open simple dialog
716
        const std::string& vKey,           // key dialog
717
        const std::string& vTitle,         // title
718
        const char* vFilters,              // filters, if null, will display only directories
719
        const FileDialogConfig& vConfig);  // FileDialogConfig
720
    const FileDialogConfig& getDialogConfig() const;
721
    FileDialogConfig& getDialogConfigRef();
722
};
723
724
class IGFD_API ThumbnailFeature {
725
protected:
726
    ThumbnailFeature();
727
    ~ThumbnailFeature();
728
729
    void m_NewThumbnailFrame(FileDialogInternal& vFileDialogInternal);
730
    void m_EndThumbnailFrame(FileDialogInternal& vFileDialogInternal);
731
    void m_QuitThumbnailFrame(FileDialogInternal& vFileDialogInternal);
732
733
#ifdef USE_THUMBNAILS
734
public:
735
    typedef std::function<void(IGFD_Thumbnail_Info*)> CreateThumbnailFun;   // texture 2d creation function binding
736
    typedef std::function<void(IGFD_Thumbnail_Info*)> DestroyThumbnailFun;  // texture 2d destroy function binding
737
738
protected:
739
    enum class DisplayModeEnum { FILE_LIST = 0, THUMBNAILS_LIST, THUMBNAILS_GRID };
740
741
private:
742
    uint32_t m_CountFiles                                    = 0U;
743
    bool m_IsWorking                                         = false;
744
    std::shared_ptr<std::thread> m_ThumbnailGenerationThread = nullptr;
745
    std::list<std::shared_ptr<FileInfos> > m_ThumbnailFileDatasToGet;  // base container
746
    std::mutex m_ThumbnailFileDatasToGetMutex;
747
    std::condition_variable m_ThumbnailFileDatasToGetCv;
748
    std::list<std::shared_ptr<FileInfos> > m_ThumbnailToCreate;  // base container
749
    std::mutex m_ThumbnailToCreateMutex;
750
    std::list<IGFD_Thumbnail_Info> m_ThumbnailToDestroy;  // base container
751
    std::mutex m_ThumbnailToDestroyMutex;
752
753
    CreateThumbnailFun m_CreateThumbnailFun   = nullptr;
754
    DestroyThumbnailFun m_DestroyThumbnailFun = nullptr;
755
756
protected:
757
    DisplayModeEnum m_DisplayMode = DisplayModeEnum::FILE_LIST;
758
759
private:
760
    void m_VariadicProgressBar(float fraction, const ImVec2& size_arg, const char* fmt, ...);
761
762
protected:
763
    // will be call in cpu zone (imgui computations, will call a texture file retrieval thread)
764
    void m_StartThumbnailFileDatasExtraction();                               // start the thread who will get byte buffer from image files
765
    bool m_StopThumbnailFileDatasExtraction();                                // stop the thread who will get byte buffer from image files
766
    void m_ThreadThumbnailFileDatasExtractionFunc();                          // the thread who will get byte buffer from image files
767
    void m_DrawThumbnailGenerationProgress();                                 // a little progressbar who will display the texture gen status
768
    void m_AddThumbnailToLoad(const std::shared_ptr<FileInfos>& vFileInfos);  // add texture to load in the thread
769
    void m_AddThumbnailToCreate(const std::shared_ptr<FileInfos>& vFileInfos);
770
    void m_AddThumbnailToDestroy(const IGFD_Thumbnail_Info& vIGFD_Thumbnail_Info);
771
    void m_DrawDisplayModeToolBar();  // draw display mode toolbar (file list, thumbnails list, small thumbnails grid, big thumbnails grid)
772
    void m_ClearThumbnails(FileDialogInternal& vFileDialogInternal);
773
774
public:
775
    void SetCreateThumbnailCallback(const CreateThumbnailFun& vCreateThumbnailFun);
776
    void SetDestroyThumbnailCallback(const DestroyThumbnailFun& vCreateThumbnailFun);
777
778
    // must be call in gpu zone (rendering, possibly one rendering thread)
779
    void ManageGPUThumbnails();  // in gpu rendering zone, whill create or destroy texture
780
#endif
781
};
782
783
class IGFD_API PlacesFeature {
784
protected:
785
    PlacesFeature();
786
787
#ifdef USE_PLACES_FEATURE
788
private:
789
    struct PlaceStruct {
790
        std::string name;  // name of the place
791
        // todo: the path could be relative, better if the app is moved but place path can be outside of the app
792
        std::string path;        // absolute path of the place
793
        bool canBeSaved = true;  // defined by code, can be used for prevent serialization / deserialization
794
        FileStyle style;
795
        float thickness = 0.0f;  // when more than 0.0f, is a separator
796
    };
797
798
    struct GroupStruct {
799
        bool canBeSaved                              = false;  // defined by code, can be used for prevent serialization / deserialization
800
        size_t displayOrder                          = 0U;     // the display order will be usedf first, then alphanumeric
801
        bool defaultOpened                           = false;  // the group is opened by default
802
        bool canBeEdited                             = false;  // will show +/- button for add/remove place in the group
803
        char editBuffer[MAX_FILE_DIALOG_NAME_BUFFER] = "";     // temp buffer for name edition
804
        int32_t selectedPlaceForEdition              = -1;
805
        ImGuiTreeNodeFlags collapsingHeaderFlag      = ImGuiTreeNodeFlags_None;
806
        ImGuiListClipper clipper;           // the list clipper of the grou
807
        std::string name;                   // the group name, will be displayed
808
        std::vector<PlaceStruct> places;    // the places (name + path)
809
        bool AddPlace(                      // add a place by code
810
            const std::string& vPlaceName,  // place name
811
            const std::string& vPlacePath,  // place path
812
            const bool vCanBeSaved,        // prevent serialization
813
            const FileStyle& vStyle = {});  // style
814
        void AddPlaceSeparator(const float& vThickness = 1.0f);
815
        bool RemovePlace(                    // remove a place by code, return true if succeed
816
            const std::string& vPlaceName);  // place name to remove
817
    };
818
819
private:
820
    std::unordered_map<std::string, std::shared_ptr<GroupStruct> > m_Groups;
821
    std::map<size_t, std::weak_ptr<GroupStruct> > m_OrderedGroups;
822
823
protected:
824
    float m_PlacesPaneWidth = 200.0f;
825
    bool m_PlacesPaneShown  = false;
826
827
protected:
828
    void m_InitPlaces(FileDialogInternal& vFileDialogInternal);
829
    void m_DrawPlacesButton();                                                            // draw place button
830
    bool m_DrawPlacesPane(FileDialogInternal& vFileDialogInternal, const ImVec2& vSize);  // draw place Pane
831
832
public:
833
    std::string SerializePlaces(                                    // serialize place : return place buffer to save in a file
834
        const bool vForceSerialisationForAll = true);              // for avoid serialization of places with flag 'canBeSaved to false'
835
    void DeserializePlaces(                                         // deserialize place : load place buffer to load in the dialog (saved from
836
        const std::string& vPlaces);                                // previous use with SerializePlaces()) place buffer to load
837
    bool AddPlacesGroup(                                            // add a group
838
        const std::string& vGroupName,                              // the group name
839
        const size_t& vDisplayOrder,                                // the display roder of the group
840
        const bool vCanBeEdited     = false,                       // let the user add/remove place in the group
841
        const bool vOpenedByDefault = true);                       // hte group is opened by default
842
    bool RemovePlacesGroup(const std::string& vGroupName);          // remove the group
843
    GroupStruct* GetPlacesGroupPtr(const std::string& vGroupName);  // get the group, if not existed, will be created
844
#endif                                                              // USE_PLACES_FEATURE
845
};
846
847
// file localization by input chat // widget flashing
848
class IGFD_API KeyExplorerFeature {
849
protected:
850
    KeyExplorerFeature();
851
852
#ifdef USE_EXPLORATION_BY_KEYS
853
private:
854
    bool m_LocateFileByInputChar_lastFound               = false;
855
    ImWchar m_LocateFileByInputChar_lastChar             = 0;
856
    float m_FlashAlpha                                   = 0.0f;  // flash when select by char
857
    float m_FlashAlphaAttenInSecs                        = 1.0f;  // fps display dependant
858
    int m_LocateFileByInputChar_InputQueueCharactersSize = 0;
859
    size_t m_FlashedItem                                 = 0;  // flash when select by char
860
    size_t m_LocateFileByInputChar_lastFileIdx           = 0;
861
862
protected:
863
    void m_LocateByInputKey(FileDialogInternal& vFileDialogInternal);  // select a file line in listview according to char key
864
    bool m_LocateItem_Loop(FileDialogInternal& vFileDialogInternal,
865
                           ImWchar vC);  // restrat for start of list view if not found a corresponding file
866
    void m_ExploreWithkeys(FileDialogInternal& vFileDialogInternal,
867
                           ImGuiID vListViewID);  // select file/directory line in listview accroding to up/down enter/backspace keys
868
    void m_StartFlashItem(size_t vIdx);           // define than an item must be flashed
869
    bool m_BeginFlashItem(size_t vIdx);           // start the flashing of a line in lsit view
870
    static void m_EndFlashItem();                 // end the fleshing accrdoin to var m_FlashAlphaAttenInSecs
871
    static bool m_FlashableSelectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, bool vFlashing = false,
872
                                      const ImVec2& size = ImVec2(0, 0));  // custom flashing selectable widgets, for flash the selected line in a short time
873
874
public:
875
    void SetFlashingAttenuationInSeconds(  // set the flashing time of the line in file list when use exploration keys
876
        float vAttenValue);                // set the attenuation (from flashed to not flashed) in seconds
877
#endif                                     // USE_EXPLORATION_BY_KEYS
878
};
879
880
class IGFD_API FileDialog : public PlacesFeature, public KeyExplorerFeature, public ThumbnailFeature {
881
protected:
882
    FileDialogInternal m_FileDialogInternal;
883
    ImGuiListClipper m_FileListClipper;
884
    ImGuiListClipper m_PathListClipper;
885
    float prOkCancelButtonWidth = 0.0f;
886
    ImGuiWindowFlags m_CurrentDisplayedFlags;
887
888
public:
889
#ifndef NEW_SINGLETON
890
    // Singleton for easier accces form anywhere but only one dialog at a time
891
    // vCopy or vForce can be used for share a memory pointer in a new memory space like a dll module
892
161k
    static FileDialog* Instance(FileDialog* vCopy = nullptr, bool vForce = false) {
893
161k
        static FileDialog _instance;
894
161k
        static FileDialog* _instance_copy = nullptr;
895
161k
        if (vCopy || vForce) {
896
0
            _instance_copy = vCopy;
897
0
        }
898
161k
        if (_instance_copy) {
899
0
            return _instance_copy;
900
0
        }
901
161k
        return &_instance;
902
161k
    }
903
#else   // NEW_SINGLETON
904
    static std::unique_ptr<FileDialog>& initSingleton(FileDialog* vCopy = nullptr, bool vForce = false) {
905
        static auto mp_instance = std::unique_ptr<FileDialog>(new FileDialog());
906
        static std::unique_ptr<FileDialog> mp_instance_copy;
907
        if (vCopy != nullptr || vForce) {
908
            if (vCopy != nullptr) {
909
                mp_instance_copy = std::unique_ptr<FileDialog>(vCopy);
910
            } else {
911
                mp_instance_copy.release();  // frees the internal borrowed pointer without deletion
912
            }
913
        }
914
        if (mp_instance_copy != nullptr) {
915
            return mp_instance_copy;
916
        }
917
        return mp_instance;
918
    }
919
    static FileDialog& ref() { return *initSingleton().get(); }
920
    static void unitSingleton() {
921
        initSingleton(nullptr, true); // frees the borrowed pointer in case
922
        initSingleton().reset(); // else the reset with destroy the borrowed pointer
923
    }
924
#endif  // NEW_SINGLETON
925
926
public:
927
    FileDialog();           // ImGuiFileDialog Constructor. can be used for have many dialog at same time (not possible with singleton)
928
    virtual ~FileDialog();  // ImGuiFileDialog Destructor
929
930
    //  standard dialog
931
    virtual void OpenDialog(                    // open simple dialog
932
        const std::string& vKey,                // key dialog
933
        const std::string& vTitle,              // title
934
        const char* vFilters,                   // filters, if null, will display only directories
935
        const FileDialogConfig& vConfig = {});  // FileDialogConfig
936
937
    // Display / Close dialog form
938
    bool Display(                                               // Display the dialog. return true if a result was obtained (Ok or not)
939
        const std::string& vKey,                                // key dialog to display (if not the same key as defined by OpenDialog => no opening)
940
        ImGuiWindowFlags vFlags = ImGuiWindowFlags_NoCollapse,  // ImGuiWindowFlags
941
        ImVec2 vMinSize         = ImVec2(0, 0),                 // mininmal size contraint for the ImGuiWindow
942
        ImVec2 vMaxSize         = ImVec2(FLT_MAX, FLT_MAX));    // maximal size contraint for the ImGuiWindow
943
944
    void Close();  // close dialog
945
946
    // queries
947
    bool WasOpenedThisFrame(const std::string& vKey) const;  // say if the dialog key was already opened this frame
948
    bool WasOpenedThisFrame() const;                         // say if the dialog was already opened this frame
949
    bool IsOpened(const std::string& vKey) const;            // say if the key is opened
950
    bool IsOpened() const;                                   // say if the dialog is opened somewhere
951
    std::string GetOpenedKey() const;                        // return the dialog key who is opened, return nothing if not opened
952
953
    // get result
954
    bool IsOk() const;                                                                                       // true => Dialog Closed with Ok result / false : Dialog closed with cancel result
955
    std::map<std::string, std::string> GetSelection(IGFD_ResultMode vFlag = IGFD_ResultMode_KeepInputFile);  // Open File behavior : will return selection via a
956
                                                                                                             // map<FileName, FilePathName>
957
    std::string GetFilePathName(IGFD_ResultMode vFlag = IGFD_ResultMode_AddIfNoFileExt);                     // Save File behavior : will return the current file path name
958
    std::string GetCurrentFileName(IGFD_ResultMode vFlag = IGFD_ResultMode_AddIfNoFileExt);                  // Save File behavior : will return the content file name
959
    std::string GetCurrentPath();                                                                            // will return current file path
960
    std::string GetCurrentFilter();                                                                          // will return current filter
961
    UserDatas GetUserDatas() const;                                                                          // will return user datas send with Open Dialog
962
963
    // file style by extentions
964
    void SetFileStyle(                                        // SetExtention datas for have custom display of particular file type
965
        const IGFD_FileStyleFlags& vFlags,                    // file style
966
        const char* vCriteria,                                // extention filter to tune
967
        const FileStyle& vInfos);                             // Filter Extention Struct who contain Color and Icon/Text for the display of the
968
                                                              // file with extention filter
969
    void SetFileStyle(                                        // SetExtention datas for have custom display of particular file type
970
        const IGFD_FileStyleFlags& vFlags,                    // file style
971
        const char* vCriteria,                                // extention filter to tune
972
        const ImVec4& vColor,                                 // wanted color for the display of the file with extention filter
973
        const std::string& vIcon = "",                        // wanted text or icon of the file with extention filter
974
        ImFont* vFont            = nullptr);                             // wanted font
975
    void SetFileStyle(FileStyle::FileStyleFunctor vFunctor);  // set file style via lambda function
976
    bool GetFileStyle(                                        // GetExtention datas. return true is extention exist
977
        const IGFD_FileStyleFlags& vFlags,                    // file style
978
        const std::string& vCriteria,                         // extention filter (same as used in SetExtentionInfos)
979
        ImVec4* vOutColor,                                    // color to retrieve
980
        std::string* vOutIcon = nullptr,                      // icon or text to retrieve
981
        ImFont** vOutFont     = nullptr);                         // font to retreive
982
    void ClearFilesStyle();                                   // clear extentions setttings
983
984
    void SetLocales(                      // set locales to use before and after the dialog display
985
        const int& vLocaleCategory,       // set local category
986
        const std::string& vLocaleBegin,  // locale to use at begining of the dialog display
987
        const std::string& vLocaleEnd);   // locale to use at the end of the dialog display
988
989
protected:
990
    void m_NewFrame();   // new frame just at begining of display
991
    void m_EndFrame();   // end frame just at end of display
992
    void m_QuitFrame();  // quit frame when qui quit the dialog
993
994
    // others
995
    bool m_Confirm_Or_OpenOverWriteFileDialog_IfNeeded(bool vLastAction, ImGuiWindowFlags vFlags);  // treatment of the result, start the confirm to overwrite dialog
996
                                                                                                    // if needed (if defined with flag)
997
998
    // dialog parts
999
    virtual void m_DrawHeader();   // draw header part of the dialog (place btn, dir creation, path composer, search bar)
1000
    virtual void m_DrawContent();  // draw content part of the dialog (place pane, file list, side pane)
1001
    virtual bool m_DrawFooter();   // draw footer part of the dialog (file field, fitler combobox, ok/cancel btn's)
1002
1003
    // widgets components
1004
    virtual void m_DisplayPathPopup(ImVec2 vSize);  // draw path popup when click on a \ or /
1005
    virtual bool m_DrawValidationButtons();         // draw validations btns, ok, cancel buttons
1006
    virtual bool m_DrawOkButton();                  // draw ok button
1007
    virtual bool m_DrawCancelButton();              // draw cancel button
1008
    virtual void m_DrawSidePane(float vHeight);     // draw side pane
1009
    virtual bool m_Selectable(int vRowIdx, const char* vLabel, bool vSelected, ImGuiSelectableFlags vFlags, const ImVec2& vSizeArg);
1010
    virtual void m_SelectableItem(int vRowIdx, std::shared_ptr<FileInfos> vInfos, bool vSelected, const char* vFmt, ...);  // draw a custom selectable behavior item
1011
    virtual void m_drawColumnText(int vColIdx, const char* vFmt, const char* vLabel, bool vSelected, bool vHovered);
1012
    virtual void m_rightAlignText(const char* text, const char* maxWidthText);  // align a text on right
1013
    virtual void m_DrawFileListView(ImVec2 vSize);  // draw file list view (default mode)
1014
1015
#ifdef USE_THUMBNAILS
1016
    virtual void m_DrawThumbnailsListView(ImVec2 vSize);  // draw file list view with small thumbnails on the same line
1017
    virtual void m_DrawThumbnailsGridView(ImVec2 vSize);  // draw a grid of small thumbnails
1018
#endif
1019
1020
    // to be called only by these function and theirs overrides
1021
    // - m_DrawFileListView
1022
    // - m_DrawThumbnailsListView
1023
    // - m_DrawThumbnailsGridView
1024
    void m_BeginFileColorIconStyle(std::shared_ptr<FileInfos> vFileInfos, bool& vOutShowColor, std::string& vOutStr,
1025
                                   ImFont** vOutFont);                    // begin style apply of filter with color an icon if any
1026
    void m_EndFileColorIconStyle(const bool vShowColor, ImFont* vFont);  // end style apply of filter
1027
1028
    void m_DisplayFileInfosTooltip(const int32_t& vRowIdx, const int32_t& vColumnIdx, std::shared_ptr<FileInfos> vFileInfos);
1029
};
1030
1031
}  // namespace IGFD
1032
1033
#endif  // __cplusplus
1034
1035
/////////////////////////////////////////////////
1036
////// C LANG API ///////////////////////////////
1037
/////////////////////////////////////////////////
1038
1039
#ifdef __cplusplus
1040
#define IGFD_C_API extern "C" IGFD_API
1041
typedef IGFD::UserDatas IGFDUserDatas;
1042
typedef IGFD::PaneFun IGFDPaneFun;
1043
typedef IGFD::FileDialog ImGuiFileDialog;
1044
#else  // __cplusplus
1045
#define IGFD_C_API
1046
typedef struct ImGuiFileDialog ImGuiFileDialog;
1047
typedef struct IGFD_Selection_Pair IGFD_Selection_Pair;
1048
typedef struct IGFD_Selection IGFD_Selection;
1049
#endif  // __cplusplus
1050
1051
typedef void (*IGFD_PaneFun)(const char*, void*, bool*);  // callback fucntion for display the pane
1052
1053
struct IGFD_FileDialog_Config {
1054
    const char* path;            // path
1055
    const char* fileName;        // defaut file name
1056
    const char* filePathName;    // if not empty, the filename and the path will be obtained from filePathName
1057
    int32_t countSelectionMax;   // count selection max
1058
    void* userDatas;             // user datas (can be retrieved in pane)
1059
    IGFD_PaneFun sidePane;       // side pane callback
1060
    float sidePaneWidth;         // side pane width};
1061
    ImGuiFileDialogFlags flags;  // ImGuiFileDialogFlags
1062
};
1063
IGFD_C_API struct IGFD_FileDialog_Config IGFD_FileDialog_Config_Get();  // return an initialized IGFD_FileDialog_Config
1064
1065
struct IGFD_Selection_Pair {
1066
    char* fileName;
1067
    char* filePathName;
1068
};
1069
1070
IGFD_C_API IGFD_Selection_Pair IGFD_Selection_Pair_Get();                                  // return an initialized IGFD_Selection_Pair
1071
IGFD_C_API void IGFD_Selection_Pair_DestroyContent(IGFD_Selection_Pair* vSelection_Pair);  // destroy the content of a IGFD_Selection_Pair
1072
1073
struct IGFD_Selection {
1074
    IGFD_Selection_Pair* table;  // 0
1075
    size_t count;                // 0U
1076
};
1077
1078
IGFD_C_API IGFD_Selection IGFD_Selection_Get();                             // return an initialized IGFD_Selection
1079
IGFD_C_API void IGFD_Selection_DestroyContent(IGFD_Selection* vSelection);  // destroy the content of a IGFD_Selection
1080
1081
// constructor / destructor
1082
IGFD_C_API ImGuiFileDialog* IGFD_Create(void);               // create the filedialog context
1083
IGFD_C_API void IGFD_Destroy(ImGuiFileDialog* vContextPtr);  // destroy the filedialog context
1084
1085
#ifdef USE_THUMBNAILS
1086
typedef void (*IGFD_CreateThumbnailFun)(IGFD_Thumbnail_Info*);   // callback function for create thumbnail texture
1087
typedef void (*IGFD_DestroyThumbnailFun)(IGFD_Thumbnail_Info*);  // callback fucntion for destroy thumbnail texture
1088
#endif                                                           // USE_THUMBNAILS
1089
1090
IGFD_C_API void IGFD_OpenDialog(                   // open a standard dialog
1091
    ImGuiFileDialog* vContextPtr,                  // ImGuiFileDialog context
1092
    const char* vKey,                              // key dialog
1093
    const char* vTitle,                            // title
1094
    const char* vFilters,                          // filters/filter collections. set it to null for directory mode
1095
    const struct IGFD_FileDialog_Config vConfig);  // config
1096
1097
IGFD_C_API bool IGFD_DisplayDialog(  // Display the dialog
1098
    ImGuiFileDialog* vContextPtr,    // ImGuiFileDialog context
1099
    const char* vKey,                // key dialog to display (if not the same key as defined by OpenDialog => no opening)
1100
    ImGuiWindowFlags vFlags,         // ImGuiWindowFlags
1101
    ImVec2 vMinSize,                 // mininmal size contraint for the ImGuiWindow
1102
    ImVec2 vMaxSize);                // maximal size contraint for the ImGuiWindow
1103
1104
IGFD_C_API void IGFD_CloseDialog(   // Close the dialog
1105
    ImGuiFileDialog* vContextPtr);  // ImGuiFileDialog context
1106
1107
IGFD_C_API bool IGFD_IsOk(          // true => Dialog Closed with Ok result / false : Dialog closed with cancel result
1108
    ImGuiFileDialog* vContextPtr);  // ImGuiFileDialog context
1109
1110
IGFD_C_API bool IGFD_WasKeyOpenedThisFrame(  // say if the dialog key was already opened this frame
1111
    ImGuiFileDialog* vContextPtr,            // ImGuiFileDialog context
1112
    const char* vKey);
1113
1114
IGFD_C_API bool IGFD_WasOpenedThisFrame(  // say if the dialog was already opened this frame
1115
    ImGuiFileDialog* vContextPtr);        // ImGuiFileDialog context
1116
1117
IGFD_C_API bool IGFD_IsKeyOpened(    // say if the dialog key is opened
1118
    ImGuiFileDialog* vContextPtr,    // ImGuiFileDialog context
1119
    const char* vCurrentOpenedKey);  // the dialog key
1120
1121
IGFD_C_API bool IGFD_IsOpened(      // say if the dialog is opened somewhere
1122
    ImGuiFileDialog* vContextPtr);  // ImGuiFileDialog context
1123
1124
IGFD_C_API IGFD_Selection IGFD_GetSelection(  // Open File behavior : will return selection via a map<FileName, FilePathName>
1125
    ImGuiFileDialog* vContextPtr,             // user datas (can be retrieved in pane)
1126
    IGFD_ResultMode vMode);                   // Result Mode
1127
1128
IGFD_C_API char* IGFD_GetFilePathName(  // Save File behavior : will always return the content of the field with current
1129
                                        // filter extention and current path, WARNINGS you are responsible to free it
1130
    ImGuiFileDialog* vContextPtr,       // ImGuiFileDialog context
1131
    IGFD_ResultMode vMode);             // Result Mode
1132
1133
IGFD_C_API char* IGFD_GetCurrentFileName(  // Save File behavior : will always return the content of the field with
1134
                                           // current filter extention, WARNINGS you are responsible to free it
1135
    ImGuiFileDialog* vContextPtr,          // ImGuiFileDialog context
1136
    IGFD_ResultMode vMode);                // Result Mode
1137
1138
IGFD_C_API char* IGFD_GetCurrentPath(  // will return current path, WARNINGS you are responsible to free it
1139
    ImGuiFileDialog* vContextPtr);     // ImGuiFileDialog context
1140
1141
IGFD_C_API char* IGFD_GetCurrentFilter(  // will return selected filter, WARNINGS you are responsible to free it
1142
    ImGuiFileDialog* vContextPtr);       // ImGuiFileDialog context
1143
1144
IGFD_C_API void* IGFD_GetUserDatas(  // will return user datas send with Open Dialog
1145
    ImGuiFileDialog* vContextPtr);   // ImGuiFileDialog context
1146
1147
IGFD_C_API void IGFD_SetFileStyle(        // SetExtention datas for have custom display of particular file type
1148
    ImGuiFileDialog* vContextPtr,         // ImGuiFileDialog context
1149
    IGFD_FileStyleFlags vFileStyleFlags,  // file style type
1150
    const char* vFilter,                  // extention filter to tune
1151
    ImVec4 vColor,                        // wanted color for the display of the file with extention filter
1152
    const char* vIconText,                // wanted text or icon of the file with extention filter (can be sued with font icon)
1153
    ImFont* vFont);                       // wanted font pointer
1154
1155
IGFD_C_API void IGFD_SetFileStyle2(       // SetExtention datas for have custom display of particular file type
1156
    ImGuiFileDialog* vContextPtr,         // ImGuiFileDialog context
1157
    IGFD_FileStyleFlags vFileStyleFlags,  // file style type
1158
    const char* vFilter,                  // extention filter to tune
1159
    float vR, float vG, float vB,
1160
    float vA,               // wanted color channels RGBA for the display of the file with extention filter
1161
    const char* vIconText,  // wanted text or icon of the file with extention filter (can be sued with font icon)
1162
    ImFont* vFont);         // wanted font pointer
1163
1164
IGFD_C_API bool IGFD_GetFileStyle(ImGuiFileDialog* vContextPtr,         // ImGuiFileDialog context
1165
                                  IGFD_FileStyleFlags vFileStyleFlags,  // file style type
1166
                                  const char* vFilter,                  // extention filter (same as used in SetExtentionInfos)
1167
                                  ImVec4* vOutColor,                    // color to retrieve
1168
                                  char** vOutIconText,                  // icon or text to retrieve, WARNINGS you are responsible to free it
1169
                                  ImFont** vOutFont);                   // font pointer to retrived
1170
1171
IGFD_C_API void IGFD_ClearFilesStyle(  // clear extentions setttings
1172
    ImGuiFileDialog* vContextPtr);     // ImGuiFileDialog context
1173
1174
IGFD_C_API void SetLocales(        // set locales to use before and after display
1175
    ImGuiFileDialog* vContextPtr,  // ImGuiFileDialog context
1176
    const int vCategory,           // set local category
1177
    const char* vBeginLocale,      // locale to use at begining of the dialog display
1178
    const char* vEndLocale);       // locale to set at end of the dialog display
1179
1180
#ifdef USE_EXPLORATION_BY_KEYS
1181
IGFD_C_API void IGFD_SetFlashingAttenuationInSeconds(  // set the flashing time of the line in file list when use
1182
                                                       // exploration keys
1183
    ImGuiFileDialog* vContextPtr,                      // ImGuiFileDialog context
1184
    float vAttenValue);                                // set the attenuation (from flashed to not flashed) in seconds
1185
#endif
1186
1187
#ifdef USE_PLACES_FEATURE
1188
IGFD_C_API char* IGFD_SerializePlaces(    // serialize place : return place buffer to save in a file, WARNINGS
1189
                                          // you are responsible to free it
1190
    ImGuiFileDialog* vContextPtr,         // ImGuiFileDialog context
1191
    bool vDontSerializeCodeBasedPlaces);  // for avoid serialization of place added by code
1192
1193
IGFD_C_API void IGFD_DeserializePlaces(  // deserialize place : load bookmar buffer to load in the dialog (saved
1194
                                         // from previous use with SerializePlaces())
1195
    ImGuiFileDialog* vContextPtr,        // ImGuiFileDialog context
1196
    const char* vPlaces);                // place buffer to load
1197
1198
IGFD_C_API bool IGFD_AddPlacesGroup(  // add a places group by code
1199
    ImGuiFileDialog* vContextPtr,     // ImGuiFileDialog context
1200
    const char* vGroupName,           // the group name
1201
    size_t vDisplayOrder,             // the display roder of the group
1202
    bool vCanBeEdited);               // let the user add/remove place in the group
1203
1204
IGFD_C_API bool IGFD_RemovePlacesGroup(  // remove a place group by code, return true if succeed
1205
    ImGuiFileDialog* vContextPtr,        // ImGuiFileDialog context
1206
    const char* vGroupName);             // place name to remove
1207
1208
IGFD_C_API bool IGFD_AddPlace(     // add a place by code
1209
    ImGuiFileDialog* vContextPtr,  // ImGuiFileDialog context
1210
    const char* vGroupName,        // the group name
1211
    const char* vPlaceName,        // place name
1212
    const char* vPlacePath,        // place path
1213
    bool vCanBeSaved,              // place can be saved
1214
    const char* vIconText);        // wanted text or icon of the file with extention filter (can be used with font icon)
1215
1216
IGFD_C_API bool IGFD_RemovePlace(  // remove a place by code, return true if succeed
1217
    ImGuiFileDialog* vContextPtr,  // ImGuiFileDialog context
1218
    const char* vGroupName,        // the group name
1219
    const char* vPlaceName);       // place name to remove
1220
1221
#endif
1222
1223
#ifdef USE_THUMBNAILS
1224
IGFD_C_API void SetCreateThumbnailCallback(        // define the callback for create the thumbnails texture
1225
    ImGuiFileDialog* vContextPtr,                  // ImGuiFileDialog context
1226
    IGFD_CreateThumbnailFun vCreateThumbnailFun);  // the callback for create the thumbnails texture
1227
1228
IGFD_C_API void SetDestroyThumbnailCallback(         // define the callback for destroy the thumbnails texture
1229
    ImGuiFileDialog* vContextPtr,                    // ImGuiFileDialog context
1230
    IGFD_DestroyThumbnailFun vDestroyThumbnailFun);  // the callback for destroy the thumbnails texture
1231
1232
IGFD_C_API void ManageGPUThumbnails(  // must be call in gpu zone, possibly a thread, will call the callback for create
1233
                                      // / destroy the textures
1234
    ImGuiFileDialog* vContextPtr);    // ImGuiFileDialog context
1235
#endif                                // USE_THUMBNAILS
/Users/kiltum/projects/zxcpp/lib/vgm_decoder/include/chips/ay-3-8910.h
Line
Count
Source
1
/*
2
MIT License
3
4
Copyright (c) 2020-2021 Aleksei Dynda
5
6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12
13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
15
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
SOFTWARE.
23
*/
24
25
#pragma once
26
27
#include <stdint.h>
28
#include <stdlib.h>
29
30
enum
31
{
32
    CHIP_TYPE_AY8910 = 0x00,
33
    CHIP_TYPE_AY8912 = 0x01,
34
    CHIP_TYPE_AY8913 = 0x02,
35
    CHIP_TYPE_AY8930 = 0x03,
36
    CHIP_TYPE_AY8914 = 0x04,
37
    CHIP_TYPE_YM2149 = 0x10,
38
    CHIP_TYPE_YM3439 = 0x11,
39
    CHIP_TYPE_YMZ284 = 0x12,
40
    CHIP_TYPE_YMZ294 = 0x13,
41
    CHIP_TYPE_YM2203 = 0x20,
42
    CHIP_TYPE_YM2608 = 0x21,
43
    CHIP_TYPE_YM2610 = 0x22,
44
    CHIP_TYPE_YM2610B = 0x23,
45
};
46
47
class AY38910
48
{
49
public:
50
    AY38910() = default;
51
52
    AY38910(uint8_t chipType, uint8_t flags);
53
54
    /** Sets chip configuration */
55
    void setType( uint8_t chipType, uint8_t flags );
56
57
    /** Resets ay-3-8910 state */
58
    void reset();
59
60
    /**
61
     * Return value of given PSG register.
62
     */
63
    uint16_t read(uint8_t reg);
64
65
    /**
66
     * Set value of given PSG register.
67
     */
68
    void write(uint8_t reg, uint16_t value);
69
70
    /**
71
     * Initialize PSG by setting up a source data line (audio output stream).
72
     * Call this before starting thread.
73
     */
74
    void init();
75
76
    /** Returns left (16 bits) and right (16 bits) channels if next sound sample */
77
    uint32_t getSample();
78
79
    /** Set chip clock external frequency */
80
    void setFrequency( uint32_t frequency );
81
82
    /**
83
     * Although AY-3-8910/YM2149 emulator allows to run at lower sample frequencies,
84
     * it is recommended to leave 44100 Hz as base emulator frequency for good quality.
85
     * Setting low sample frequencies like 11025 Hz greatly reduces quality of high
86
     * frequency tones. It is recommended to do downsampling with the output of AY-3-8910
87
     * emulator.
88
     */
89
    void setSampleFrequency( uint32_t sampleFrequency );
90
91
    /** Returns currently set sample frequency */
92
0
    uint32_t getSampleFrequency() const { return m_sampleFrequency; }
93
94
    /** Changes volume level. Default level is 100! */
95
    void setVolume(uint16_t volume);
96
97
    /** TODO: */
98
    //void setStereoMode(uint8_t mode);
99
100
private:
101
    /** Chip Type. */
102
    uint8_t m_chipType = 0;
103
104
    /** Chip flags */
105
    uint8_t m_flags = 0;
106
107
    /** Random generator */
108
    uint32_t m_rng = 1;
109
110
    /** AY-3-8910 Chip frequency in HZ */
111
    uint32_t m_frequency = 3579545;
112
113
    /** Audio hardware sample frequency */
114
    uint32_t m_sampleFrequency = 44100;
115
116
    /** How many tone ticks in one audio sample */
117
    uint32_t m_toneFrequencyScale = 0;
118
119
    uint32_t m_envFrequencyScale = 0;
120
121
    /** period value for sound channels: A, B, C. */
122
    uint32_t m_period[3]{};
123
124
    /** Period value for noise. */
125
    uint32_t m_periodNoise = 0;
126
127
    /** Mixer register. */
128
    uint8_t m_mixer = 0x00;
129
130
    /** Volume value for sound channel. */
131
    uint8_t m_amplitude[3]{};
132
133
    uint8_t m_ampR[3]{};
134
135
    /** Period value for envelope. */
136
    uint32_t m_periodE = 0;
137
138
    /** Envelope register is used only for storing passed value */
139
    uint8_t m_envelopeReg = 0;
140
141
    bool m_holding = true;
142
143
    bool m_hold = false;
144
145
    bool m_attack = false;
146
147
    bool m_continue = false;
148
149
    bool m_alternate = false;
150
151
    bool m_noiseRecalc = false;
152
153
    uint8_t m_envStepMask = 0x0F;
154
155
    /** Indicates whether a particular channel is in envelope or period mode. */
156
    bool m_useEnvelope[3]{};
157
158
    /** Channel A/B/C frequency counter */
159
    uint32_t m_counter[3]{};
160
161
    /** Turn channel A/B/C on/off */
162
    bool m_channelOutput[3]{};
163
164
    /** Noise frequency counter */
165
    uint32_t m_counterNoise = 0;
166
167
    /** Turn noise on/off */
168
    bool m_noiseHigh = false;
169
170
    /** Envelope frequency counter */
171
    uint32_t m_counterEnv = 0;
172
173
    /** Envelope tick counter */
174
    uint8_t m_envVolume = 0;
175
176
    /** volume level table for the chip */
177
    uint16_t m_levelTable[32];
178
179
    /** user volume level */
180
    uint16_t m_userVolume = 100;
181
182
    /** Recalculates volume tables */
183
    void calcVolumeTables();
184
};
185
186
/Users/kiltum/projects/zxcpp/lib/vgm_decoder/src/chips/ay-3-8910.cpp
Line
Count
Source
1
/*
2
MIT License
3
4
Copyright (c) 2020-2021 Aleksei Dynda
5
6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12
13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
15
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
SOFTWARE.
23
*/
24
25
#include "chips/ay-3-8910.h"
26
27
#include <stdint.h>
28
#include <stdlib.h>
29
30
#define AY38910_DEBUG 1
31
32
#if AY38910_DEBUG && !defined(VGM_DECODER_LOGGER)
33
#define VGM_DECODER_LOGGER 1
34
#endif
35
#include "../vgm_logger.h"
36
37
38
0
#define YM2149_PIN26_LOW   (0x10)
39
40
/*
41
42
Normalized voltage
43
44
 0      0.0000
45
 1      0.0137
46
 2      0.0205
47
 3      0.0291
48
 4      0.0423
49
 5      0.0618
50
 6      0.0847
51
 7      0.1369
52
 8      0.1691
53
 9      0.2647
54
10      0.3527
55
11      0.4499
56
12      0.5704
57
13      0.6873
58
14      0.8482
59
15      1.0000
60
61
 */
62
63
static uint16_t ay8910LevelTable[16] =
64
{
65
0,  183,  408,  683,  1020, 1433, 1939, 2558, 3317, 4246, 5385, 6779, 8487, 10579,  13142,  16281,
66
/*    2345,  3017,  3945,  4820,  5985,  7190,  8590,  10600,
67
    11370, 12890, 13620, 14275, 14760, 15090, 15350, 15950,*/
68
};
69
70
static uint16_t ym2149LevelTable[32] =
71
{
72
0,  81, 171,  270,  380,  501,  635,  783,
73
947,  1128, 1327, 1548, 1793, 2062, 2361, 2690,
74
3055, 3457, 3902, 4394, 4937, 5538, 6201, 6935,
75
7746, 8642, 9632, 10726,  11936,  13272,  14749,  16382,
76
};
77
78
enum
79
{
80
/*
81
 The frequency of each square wave generated by the three tone
82
 generators (channels A, B, C) is obtained by combining the contents
83
 of the Coarse and Fine Tune registers.
84
 FTR = fine tune register (provides the lower 8 bits)
85
 CTR = coarse tune register (provides the higher 4 bits)
86
*/
87
88
    FTR_A = 0,
89
    CTR_A = 1,
90
    FTR_B = 2,
91
    CTR_B = 3,
92
    FTR_C = 4,
93
    CTR_C = 5,
94
/*
95
 Noise period register (lower 5 bits, upper bits are not used)
96
 */
97
    NPR = 6,
98
99
    R_MIXER = 7,
100
101
/*
102
 The amplitudes of the signals generated by each of the three D/A
103
 converters (channels A, B, C) is determined by the contents of the
104
 lower 5 bits of the amplitude registers:
105
 b4 = amplitude mode (modulate envelope or period)
106
 b3 - b0 = fixed amplitude level
107
*/
108
    R_AMP_A = 8,
109
    R_AMP_B = 9,
110
    R_AMP_C = 10,
111
/*
112
 The frequency of the envelope is the 16 bit envelope period value.
113
 FTC = fine tune control (provides the lower 8 bits)
114
 CTC = coarse tune control (provides the higher 8 bits)
115
*/
116
    R_FPC_E = 11,
117
    R_CPC_E = 12,
118
    R_ENVELOPE = 13,
119
    R_RS232_A = 14,
120
    R_RS232_B = 15,
121
};
122
123
124
enum
125
{
126
    CHANNEL_A = 0,
127
    CHANNEL_B = 1,
128
    CHANNEL_C = 2,
129
};
130
131
132
AY38910::AY38910(uint8_t chipType, uint8_t flags)
133
1
{
134
1
    setType( chipType, flags );
135
1
}
136
137
void AY38910::setType( uint8_t chipType, uint8_t flags )
138
1
{
139
1
    m_chipType = chipType;
140
1
    m_flags = flags;
141
1
    reset();
142
1
}
143
144
void AY38910::setFrequency( uint32_t frequency )
145
1
{
146
1
    m_frequency = frequency * 2;
147
1
    reset();
148
1
}
149
150
void AY38910::setSampleFrequency( uint32_t sampleFrequency )
151
1
{
152
1
    m_sampleFrequency = sampleFrequency;
153
1
    reset();
154
1
}
155
156
void AY38910::setVolume(uint16_t volume)
157
1
{
158
1
    m_userVolume = volume;
159
1
    calcVolumeTables();
160
1
}
161
162
void AY38910::calcVolumeTables()
163
1
{
164
1
    if ( m_chipType & 0xF0 )
165
0
    {
166
0
        for (int i = 0; i < 32; i++)
167
0
        {
168
0
            uint32_t vol = static_cast<uint32_t>( ym2149LevelTable[i] ) * m_userVolume * 60 / (100 * 32);
169
0
            if ( vol > 65535 ) vol = 65535;
170
0
            m_levelTable[i] = vol;
171
0
        }
172
0
    }
173
1
    else
174
1
    {
175
17
        for (int i = 0; i < 16; i++)
176
16
        {
177
16
            uint32_t vol = static_cast<uint32_t>( ay8910LevelTable[i] ) * m_userVolume * 60 / (100 * 32);
178
16
            if ( vol > 65535 ) vol = 65535;
179
16
            m_levelTable[i] = vol;
180
16
        }
181
1
    }
182
1
}
183
184
void AY38910::reset()
185
4
{
186
4
    m_envStepMask = ( m_chipType & 0xF0 ) ? 0x1F: 0x0F;
187
4
    m_toneFrequencyScale = (m_frequency / m_sampleFrequency); // * 16
188
4
    m_envFrequencyScale = (m_frequency / m_sampleFrequency);  // * 256
189
4
    if ( !( m_chipType & 0xF0 ) )
190
4
    {
191
4
        m_envFrequencyScale /= 2;
192
4
    }
193
194
4
    if ( ( m_chipType & 0xF0 ) && ( m_flags & YM2149_PIN26_LOW ) )
195
0
    {
196
0
        m_envFrequencyScale /= 2;
197
0
    }
198
199
60
    for (int i=0; i<=R_ENVELOPE; i++) write(i, 0);
200
4
}
201
202
uint16_t AY38910::read(uint8_t reg)
203
0
{
204
0
    switch (reg)
205
0
    {
206
0
    case FTR_A:
207
0
        return (m_period[CHANNEL_A] >> 4) & 0xFF;
208
0
    case CTR_A:
209
0
        return (m_period[CHANNEL_A] >> 12) & 0x0F;
210
0
    case FTR_B:
211
0
        return (m_period[CHANNEL_B] >> 4) & 0xFF;
212
0
    case CTR_B:
213
0
        return (m_period[CHANNEL_B] >> 12) & 0x0F;
214
0
    case FTR_C:
215
0
        return (m_period[CHANNEL_C] >> 4) & 0xFF;
216
0
    case CTR_C:
217
0
        return (m_period[CHANNEL_C] >> 12) & 0x0F;
218
0
    case NPR:
219
0
        return (m_periodNoise >> 4);
220
0
    case R_MIXER:
221
0
        return m_mixer;
222
0
    case R_AMP_A:
223
0
        return m_ampR[CHANNEL_A];
224
0
    case R_AMP_B:
225
0
        return m_ampR[CHANNEL_B];
226
0
    case R_AMP_C:
227
0
        return m_ampR[CHANNEL_C];
228
0
    case R_FPC_E:
229
0
        return (m_periodE >> 8) & 0xff;
230
0
    case R_CPC_E:
231
0
        return (m_periodE >> 16) & 0xff00;
232
0
    case R_ENVELOPE:
233
0
        return m_envelopeReg;
234
0
    case R_RS232_A:
235
0
    case R_RS232_B:
236
0
        return 0xff;
237
0
    default:
238
0
        return 0;
239
0
    }
240
0
}
241
242
void AY38910::write(uint8_t reg, uint16_t value)
243
23.7k
{
244
23.7k
    LOGI( ">> reg: %d = 0x%02X\n", reg, value);
245
23.7k
    switch (reg)
246
23.7k
    {
247
2.78k
    case FTR_A:
248
2.78k
        m_period[CHANNEL_A] = ((m_period[CHANNEL_A] & 0xf000) | (value << 4));
249
2.78k
        break;
250
2.78k
    case CTR_A:
251
2.78k
        m_period[CHANNEL_A] = (((value & 0x0f) << 12) | (m_period[CHANNEL_A] & 0xff0));
252
2.78k
        break;
253
1.52k
    case FTR_B:
254
1.52k
        m_period[CHANNEL_B] = ((m_period[CHANNEL_B] & 0xf000) | (value << 4));
255
1.52k
        break;
256
1.52k
    case CTR_B:
257
1.52k
        m_period[CHANNEL_B] = (((value & 0x0f) << 12) | (m_period[CHANNEL_B] & 0xff0));
258
1.52k
        break;
259
672
    case FTR_C:
260
672
        m_period[CHANNEL_C] = ((m_period[CHANNEL_C] & 0xf000) | (value << 4));
261
672
        break;
262
672
    case CTR_C:
263
672
        m_period[CHANNEL_C] = (((value & 0x0f) << 12) | (m_period[CHANNEL_C] & 0xff0));
264
672
        break;
265
4.55k
    case NPR:
266
4.55k
        m_periodNoise = (value & 0x1f) << 4;
267
4.55k
        break;
268
4.54k
    case R_MIXER:
269
4.54k
        m_mixer = value;
270
4.54k
        break;
271
272
2.75k
    case R_AMP_A:
273
4.24k
    case R_AMP_B:
274
4.67k
    case R_AMP_C:
275
4.67k
    {
276
4.67k
        uint8_t channel = reg - R_AMP_A;
277
4.67k
        m_ampR[channel] = value;
278
4.67k
        m_amplitude[channel] = (value & 0x0f);
279
        // For YM2149 type chips there 32 levels instead of 16
280
4.67k
        if ( m_chipType & 0xF0 ) m_amplitude[channel] <<= 1;
281
4.67k
        m_useEnvelope[channel] = ((value & 0x10) != 0);
282
4.67k
        break;
283
4.24k
    }
284
4
    case R_FPC_E:
285
4
        m_periodE = ((m_periodE & 0xff0000) | (value << 8));
286
4
        break;
287
4
    case R_CPC_E:
288
4
        m_periodE = ((value << 16) | (m_periodE & 0x00ff00));
289
4
        break;
290
4
    case R_ENVELOPE:
291
4
        m_envelopeReg = value;
292
4
        m_holding = false;
293
4
        m_hold = !!(value & 0x01); // true means one cycle only
294
4
        m_alternate = !!(value & 0x02); // true means change direction after each cycle
295
        // When both the Hold bit and the Alternate bit are ones, the envelope counter is reset to its initial count before holding.
296
4
        m_attack = !!(value & 0x04); // if true from 0000 to 1111, otherwise from 1111 to 0000
297
4
        m_continue = !!(value & 0x08); // if true hold bit, otherwise hold is enabled
298
4
        if ( !m_continue ) m_hold = true;
299
4
        m_envVolume = m_attack ? 0 : m_envStepMask;
300
301
4
        m_counterEnv = 0;
302
4
        break;
303
0
    case R_RS232_A:
304
0
    case R_RS232_B:
305
0
        break;
306
0
    default:
307
0
        LOGE( "Unknown register %02X\n", reg );
308
0
        break;
309
23.7k
    }
310
23.7k
}
311
312
uint32_t AY38910::getSample()
313
4.61M
{
314
18.4M
    for (int i=0; i<3; i++)
315
13.8M
    {
316
13.8M
        m_counter[i] += m_toneFrequencyScale;
317
13.8M
        if (m_counter[i] >= m_period[i])
318
2.81M
        {
319
2.81M
            m_channelOutput[i] = !m_channelOutput[i];
320
2.81M
            m_counter[i] = 0;
321
2.81M
        }
322
13.8M
    }
323
324
4.61M
    m_counterNoise += m_toneFrequencyScale;
325
4.61M
    if (m_counterNoise >= m_periodNoise)
326
2.48M
    {
327
2.48M
        m_counterNoise = 0;
328
2.48M
        m_noiseRecalc = !m_noiseRecalc;
329
2.48M
        if ( m_noiseRecalc )
330
1.24M
        {
331
            // The Random Number Generator of the 8910 is a 17-bit shift
332
            // register. The input to the shift register is bit0 XOR bit3
333
            // (bit0 is the output).
334
1.24M
            m_rng ^= (((m_rng & 1) ^ ((m_rng >> 3) & 1)) << 17);
335
1.24M
            m_rng >>= 1;
336
1.24M
            m_noiseHigh = !!(m_rng & 1);
337
1.24M
        }
338
2.48M
    }
339
340
4.61M
    if ( !m_holding )
341
4.61M
    {
342
4.61M
        if (m_periodE > 0)
343
0
        {
344
0
            m_counterEnv += m_envFrequencyScale;
345
            // The envelope counter runs at half the speed of the
346
            // tone counter, so double the envelope period
347
0
            if (m_counterEnv >= m_periodE)
348
0
            {
349
0
                m_counterEnv = 0;
350
0
                m_envVolume += m_attack ? 1: -1;
351
0
                if ( m_envVolume > m_envStepMask ) // if overflow happened, we reached the boundary: low or high
352
0
                {
353
0
                    m_holding = m_hold;
354
                    // step back
355
0
                    m_envVolume -= m_attack ? 1: -1;
356
0
                    if ( !m_continue ) m_envVolume = 0;
357
0
                    else if ( m_alternate && m_hold ) m_envVolume ^= m_envStepMask;
358
0
                    else if ( !m_hold && !m_alternate ) m_envVolume ^= m_envStepMask;
359
0
                    else if ( !m_hold && m_alternate ) m_attack = !m_attack;
360
0
                }
361
0
            }
362
0
        }
363
4.61M
    }
364
365
4.61M
    uint32_t left = 0;
366
4.61M
    uint32_t right = 0;
367
368
18.4M
    for(int chan=0; chan<3; chan++)
369
13.8M
    {
370
// Two variant for calculating enable field. Both work
371
//        bool enabled = ( ((m_mixer >> chan) & 1)  || m_channelOutput[chan] ) &&
372
//                       ( ((m_mixer >> (3 + chan)) & 1) || m_noiseHigh );
373
13.8M
        bool enabled = ( ((m_mixer >> chan) & 1) == 0 && m_channelOutput[chan] ) ||
374
13.8M
                       ( ((m_mixer >> (3 + chan)) & 1) == 0 && m_noiseHigh );
375
13.8M
        if (m_useEnvelope[chan])
376
0
        {
377
            // TODO: Evelope must have it's own table
378
0
            left += m_levelTable[enabled ? m_envVolume: 0];
379
0
            right += m_levelTable[enabled ? m_envVolume: 0];
380
0
        }
381
13.8M
        else
382
13.8M
        {
383
13.8M
            left += m_levelTable[enabled ? m_amplitude[chan]: 0];
384
13.8M
            right += m_levelTable[enabled ? m_amplitude[chan]: 0];
385
13.8M
        }
386
13.8M
    }
387
4.61M
/*    left /= 3;*/ if ( left > 65535 ) left = 65535;
388
4.61M
/*    right /= 3;*/ if ( right > 65535 ) right = 65535;
389
4.61M
    return (left<<16) | right;
390
4.61M
}
/Users/kiltum/projects/zxcpp/lib/vgm_decoder/src/vgm_logger.h
Line
Count
Source
1
/*
2
MIT License
3
4
Copyright (c) 2020 Aleksei Dynda
5
6
Permission is hereby granted, free of charge, to any person obtaining a copy
7
of this software and associated documentation files (the "Software"), to deal
8
in the Software without restriction, including without limitation the rights
9
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
copies of the Software, and to permit persons to whom the Software is
11
furnished to do so, subject to the following conditions:
12
13
The above copyright notice and this permission notice shall be included in all
14
copies or substantial portions of the Software.
15
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
SOFTWARE.
23
*/
24
25
#pragma once
26
27
/*
28
    VGM_DECODER_LOGGER accepts the following values
29
    0 - disable logging
30
    1 - enable only error logging
31
    2 - enable error and info logging
32
    3 - enable error, info and memory logging
33
*/
34
35
#ifndef VGM_DECODER_LOGGER
36
#define VGM_DECODER_LOGGER 0
37
#endif
38
39
#if VGM_DECODER_LOGGER
40
#include <stdio.h>
41
42
0
#define LOGE(...) fprintf( stderr, __VA_ARGS__ )
43
#if VGM_DECODER_LOGGER > 1
44
#define LOG(...) fprintf( stderr, __VA_ARGS__ )
45
#define LOGI(...) LOG(__VA_ARGS__)
46
#else
47
#define LOG(...)
48
#define LOGI(...)
49
#endif
50
#if VGM_DECODER_LOGGER > 2
51
#define LOGM(...) fprintf( stderr, __VA_ARGS__ )
52
#else
53
#define LOGM(...)
54
#endif
55
56
#else
57
#define LOGE(...)
58
#define LOG(...)
59
#define LOGI(...)
60
#define LOGM(...)
61
#endif
/Users/kiltum/projects/zxcpp/src/ay8912.cpp
Line
Count
Source
1
#include "ay8912.hpp"
2
#include "chips/ay-3-8910.h"
3
#include <iostream>
4
#include <cstring>
5
#include <SDL3/SDL.h>
6
#include <thread>
7
#include <chrono>
8
#include <vector>
9
#include <cmath>
10
11
1
AY8912::AY8912() : selectedRegister(0),
12
1
                   addressLatch(false),
13
1
                   audioStream(nullptr),
14
1
                   audioDevice(0),
15
1
                   initialized(false),
16
1
                   audioThreadRunning(false)
17
1
{
18
    // Initialize registers
19
1
    std::memset(registers, 0, sizeof(registers));
20
21
    // Initialize the vgm_decoder AY-3-8910 emulator
22
1
    ayChip = new AY38910(CHIP_TYPE_AY8910, 0);
23
1
    ayChip->setFrequency(1773400);     // ZX Spectrum clock frequency
24
1
    ayChip->setSampleFrequency(44100); // Audio sample frequency
25
1
    ayChip->setVolume(100);
26
1
    ayChip->reset();
27
1
}
28
29
AY8912::~AY8912()
30
1
{
31
1
    cleanup();
32
1
    if (ayChip)
33
1
    {
34
1
        delete ayChip;
35
1
        ayChip = nullptr;
36
1
    }
37
1
}
38
39
bool AY8912::initialize()
40
1
{
41
    // Check if audio subsystem is available
42
1
    if (!(SDL_WasInit(SDL_INIT_AUDIO) & SDL_INIT_AUDIO))
43
0
    {
44
0
        std::cerr << "SDL audio subsystem not initialized, skipping AY8912 sound initialization" << std::endl;
45
0
        return false;
46
0
    }
47
48
1
    SDL_AudioSpec desired_spec;
49
1
    SDL_zero(desired_spec);
50
1
    desired_spec.format = SDL_AUDIO_S16;
51
1
    desired_spec.freq = 44100;
52
1
    desired_spec.channels = 2;
53
54
1
    audioStream = SDL_CreateAudioStream(&desired_spec, &desired_spec);
55
1
    if (!audioStream)
56
0
    {
57
0
        printf("Error creating AY8912 audio stream: %s\n", SDL_GetError());
58
0
        return false;
59
0
    }
60
61
    // Create and open audio device, then bind the stream to it
62
1
    audioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desired_spec);
63
1
    if (!audioDevice)
64
0
    {
65
0
        printf("Error opening AY8912 audio device: %s\n", SDL_GetError());
66
0
        SDL_DestroyAudioStream(audioStream);
67
0
        audioStream = nullptr;
68
0
        return false;
69
0
    }
70
71
    // Bind the audio stream to the device
72
1
    if (!SDL_BindAudioStream(audioDevice, audioStream))
73
0
    {
74
0
        printf("Error binding AY8912 audio stream: %s\n", SDL_GetError());
75
0
        SDL_CloseAudioDevice(audioDevice);
76
0
        SDL_DestroyAudioStream(audioStream);
77
0
        audioStream = nullptr;
78
0
        audioDevice = 0;
79
0
        return false;
80
0
    }
81
82
    // Start audio playback
83
1
    SDL_ResumeAudioDevice(audioDevice);
84
85
    // Start audio processing thread
86
1
    audioThreadRunning = true;
87
1
    audioThread = std::thread(&AY8912::processAudio, this);
88
89
1
    initialized = true;
90
91
    // Test configuration: Set up a simple tone for testing
92
    // Configure channel A for a 1000Hz tone
93
    // Period = Clock / (16 * Frequency) = 1773400 / (16 * 1000) = 110.8375 ≈ 111
94
    // if (ayChip) {
95
    //     ayChip->write(0, 111 & 0xFF);        // FTR_A - fine tune register A (low byte)
96
    //     ayChip->write(1, (111 >> 8) & 0x0F); // CTR_A - coarse tune register A (high nibble)
97
    //     ayChip->write(7, 0xFE);              // Mixer - enable tone for channel A (bit 0 = 0), disable noise (bits 3-5 = 1)
98
    //     ayChip->write(8, 0x0F);              // Amplitude A - set maximum volume for channel A
99
    // }
100
101
1
    std::cout << "AY8912 sound system initialized successfully" << std::endl;
102
1
    return true;
103
1
}
104
105
void AY8912::cleanup()
106
2
{
107
    // Stop audio processing thread first
108
2
    if (audioThreadRunning)
109
1
    {
110
1
        audioThreadRunning = false;
111
1
        if (audioThread.joinable())
112
1
        {
113
1
            audioThread.join();
114
1
        }
115
1
    }
116
117
    // Only clean up SDL audio resources if they were successfully initialized
118
    // and if SDL is still initialized (not shut down yet)
119
2
    if (initialized && (SDL_WasInit(SDL_INIT_AUDIO) & SDL_INIT_AUDIO))
120
1
    {
121
1
        if (audioDevice)
122
1
        {
123
1
            SDL_CloseAudioDevice(audioDevice);
124
1
            audioDevice = 0;
125
1
        }
126
127
1
        if (audioStream)
128
1
        {
129
1
            SDL_DestroyAudioStream(audioStream);
130
1
            audioStream = nullptr;
131
1
        }
132
1
    }
133
134
2
    initialized = false;
135
2
}
136
137
void AY8912::reset()
138
0
{
139
    // Reset registers
140
0
    std::memset(registers, 0, sizeof(registers));
141
0
    selectedRegister = 0;
142
0
    addressLatch = false;
143
144
    // Reset the AY-3-8910 chip
145
0
    if (ayChip)
146
0
    {
147
0
        ayChip->reset();
148
0
    }
149
0
}
150
151
void AY8912::writePort(uint16_t port, uint8_t value)
152
48.8k
{
153
    // Check for address/data select port (0xFFFD) - bits 15-14 must be 11, bits 1-0 must be 01
154
48.8k
    if ((port & 0xC001) == 0xC001)
155
23.8k
    {
156
        // Write register number
157
23.8k
        selectedRegister = value & 0x0F; // Only lower 4 bits are valid
158
23.8k
        addressLatch = true;
159
23.8k
    }
160
161
    // Check for register data port (0xBFFD) - bits 15-14 must be 10, bits 1-0 must be 01
162
25.0k
    else if ((port & 0xC001) == 0x8001)
163
23.7k
    {
164
        // Write data to selected register
165
23.7k
        if (addressLatch && selectedRegister <= 13)
166
23.6k
        {
167
23.6k
            registers[selectedRegister] = value;
168
23.6k
            addressLatch = false; // Reset latch after writing data
169
170
            // Pass the register write to the AY-3-8910 emulator
171
23.6k
            if (ayChip)
172
23.6k
            {
173
23.6k
                ayChip->write(selectedRegister, value);
174
23.6k
            }
175
23.6k
        }
176
23.7k
    }
177
48.8k
}
178
179
uint8_t AY8912::readPort(uint16_t port)
180
96
{
181
    // Check for address/data select port (0xFFFD) - bits 15-14 must be 11, bits 1-0 must be 01
182
96
    if ((port & 0xC001) == 0xC001)
183
96
    {
184
96
        if (addressLatch && selectedRegister <= 13)
185
32
        {
186
            // Read from selected register
187
32
            return registers[selectedRegister];
188
32
        }
189
64
        else
190
64
        {
191
            // Return 0 if no address is latched
192
64
            return 0;
193
64
        }
194
96
    }
195
0
    return 0;
196
96
}
197
198
void AY8912::processAudio()
199
1
{
200
1
    const int bufferSize = 1024;                      // Process 1024 samples at a time
201
1
    std::vector<int16_t> audioBuffer(bufferSize * 2); // Stereo samples
202
203
4.50k
    while (audioThreadRunning)
204
4.50k
    {
205
4.50k
        if (initialized && audioStream && ayChip)
206
4.50k
        {
207
            // Generate audio samples using the AY-3-8910 emulator
208
4.50k
            bool silentAudio = true;
209
4.61M
            for (int i = 0; i < bufferSize; i++)
210
4.61M
            {
211
4.61M
                uint32_t sample = ayChip->getSample();
212
213
                // Convert unsigned 16-bit to signed 16-bit
214
4.61M
                int16_t left = static_cast<int16_t>(((sample >> 16) & 0xFFFF) - 32768);
215
4.61M
                int16_t right = static_cast<int16_t>((sample & 0xFFFF) - 32768);
216
4.61M
                audioBuffer[i * 2] = left;           // Left channel
217
4.61M
                audioBuffer[i * 2 + 1] = right;      // Right channel
218
4.61M
                if (left > -32768 || right > -32768) // we should not overflow audio by silence samples
219
1.86M
                    silentAudio = false;
220
4.61M
            }
221
222
            // Put audio data into the stream
223
4.50k
            if (!silentAudio)
224
2.33k
                SDL_PutAudioStreamData(audioStream, audioBuffer.data(), bufferSize * 2 * sizeof(int16_t));
225
4.50k
        }
226
227
        // For 44100 Hz sample rate and 1024 samples, we need to sleep for about 23ms
228
4.50k
        std::this_thread::sleep_for(std::chrono::microseconds(19000));
229
4.50k
    }
230
1
}
/Users/kiltum/projects/zxcpp/src/emulator.cpp
Line
Count
Source
1
#include <SDL3/SDL.h>
2
#include <iostream>
3
#include <memory>
4
#include <algorithm>
5
#include <thread>
6
#include <atomic>
7
#include <chrono>
8
#include <mutex>
9
#include "ula.hpp"
10
#include "memory.hpp"
11
#include "port.hpp"
12
#include "z80.hpp"
13
#include "kempston.hpp"
14
#include "sound.hpp"
15
#include "tape.hpp"
16
#include "ay8912.hpp"
17
18
// ImGui includes
19
#include "imgui.h"
20
#include "imgui_impl_sdl3.h"
21
#include "imgui_impl_sdlrenderer3.h"
22
#include "ImGuiFileDialog.h"
23
24
class Emulator
25
{
26
private:
27
    // SDL graphics components
28
    SDL_Window *window;     // Main window for the emulator
29
    SDL_Renderer *renderer; // Renderer for drawing graphics
30
    SDL_Texture *texture;   // Texture for the screen buffer
31
    bool quit;              // Flag to signal when to exit the emulator
32
33
    // Thread management for running the emulation in the background
34
    std::thread emulationThread;     // Thread that runs the Z80 CPU emulation
35
    std::atomic<bool> threadRunning; // Atomic flag to safely control thread execution
36
37
    // Emulator hardware components (each represents a real ZX Spectrum chip or subsystem)
38
    std::unique_ptr<Memory> memory;     // RAM and ROM memory management
39
    std::unique_ptr<Port> ports;        // Input/output port handling
40
    std::unique_ptr<Z80> cpu;           // Zilog Z80 CPU emulation
41
    std::unique_ptr<ULA> ula;           // Sinclair ULA (graphics and keyboard controller)
42
    std::unique_ptr<Kempston> kempston; // Kempston joystick interface
43
    std::unique_ptr<Sound> sound;       // Beeper sound system
44
    std::unique_ptr<Tape> tape;         // Tape loading system
45
    std::unique_ptr<AY8912> ay8912;     // AY-3-8912 sound chip (for better sound)
46
47
    // Thread synchronization for safely sharing data between threads
48
    std::mutex screenMutex; // Mutex to protect screen data when updating from different threads
49
    bool screenUpdated;     // Flag to indicate when the screen has been updated
50
51
    // Screen update rate limiting to prevent overwhelming the display during fast operations
52
    std::chrono::high_resolution_clock::time_point lastScreenUpdate; // Last time screen was updated
53
    const std::chrono::milliseconds minScreenUpdateInterval{100};    // Minimum time between screen updates (10 FPS)
54
55
    // Keyboard handling functions
56
    void handleKeyDown(SDL_Keycode key); // Process key press events
57
    void handleKeyUp(SDL_Keycode key);   // Process key release events
58
59
    // Change machine type: true for 48k, false for 128k
60
    void change48(bool is48);
61
62
    // Start tape playback
63
    void StartTape();
64
65
    // CPU timing parameters
66
    int TARGET_FREQUENCY; // Target CPU frequency in Hz
67
    int CHECK_INTERVAL;   // How often to check timing (in CPU cycles)
68
69
public:
70
    // Constructor - initializes all pointers to null/false
71
    // This is called when an Emulator object is created
72
    Emulator()
73
1
    {
74
        // Initialize all SDL components to null pointers
75
        // These will be properly allocated in the initialize() method
76
1
        window = nullptr;
77
1
        renderer = nullptr;
78
1
        texture = nullptr;
79
80
        // Initialize flags to false
81
        // These control the state of our emulator
82
1
        quit = false;          // Not ready to quit yet
83
1
        threadRunning = false; // Emulation thread not running yet
84
1
        screenUpdated = false; // Screen hasn't been updated yet
85
1
    }
86
87
    // Run emulation in a separate thread
88
    void runZX();
89
90
    // Public methods for command line tape loading
91
    bool loadTapeFile(const std::string &filePath);
92
    void startTapePlayback();
93
94
    // Destructor - automatically cleans up when Emulator object is destroyed
95
    ~Emulator()
96
1
    {
97
1
        cleanup();
98
1
    }
99
100
    bool initialize()
101
1
    {
102
        // Step 1: Initialize all core emulator components
103
        // These represent the actual hardware chips in a real ZX Spectrum
104
105
        // Initialize memory system (RAM and ROM)
106
1
        memory = std::make_unique<Memory>();
107
108
        // Initialize port system (input/output connections)
109
1
        ports = std::make_unique<Port>();
110
111
        // Initialize tape loading system
112
1
        tape = std::make_unique<Tape>();
113
114
        // Initialize main processor (Z80 CPU)
115
        // Pass references to memory and ports so CPU can interact with them
116
1
        cpu = std::make_unique<Z80>(memory.get(), ports.get());
117
118
        // Initialize graphics and keyboard controller (ULA chip)
119
        // Pass references to memory and tape systems
120
1
        ula = std::make_unique<ULA>(memory.get(), tape.get());
121
122
        // Initialize joystick interface (Kempston)
123
1
        kempston = std::make_unique<Kempston>();
124
125
        // Step 2: Connect hardware components through port handlers
126
        // The ZX Spectrum uses specific port addresses to communicate with hardware
127
128
        // Connect ULA (graphics/keyboard controller) to port 0xFE
129
1
        ports->RegisterReadHandler(0xFE, [this](uint16_t port) -> uint8_t
130
9.95M
                                   { return ula->readPort(port); });
131
1
        ports->RegisterWriteHandler(0xFE, [this](uint16_t port, uint8_t value)
132
717k
                                    { ula->writePort(port, value); });
133
134
        // Connect Kempston joystick to port 0x1F
135
1
        ports->RegisterReadHandler(0x1F, [this](uint16_t port) -> uint8_t
136
1.51k
                                   { return kempston->readPort(port); });
137
138
        // Connect Memory interface to port 0xFD
139
1
        ports->RegisterWriteHandler(0xFD, [this](uint16_t port, uint8_t value)
140
48.8k
                                    { return memory->writePort(port, value); });
141
142
        // Step 3: Initialize SDL for graphics and sound
143
        // SDL is a cross-platform library for multimedia applications
144
1
        if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO))
145
0
        {
146
0
            std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
147
0
            std::cerr << "SDL Error code: " << SDL_GetError() << std::endl;
148
0
            return false;
149
0
        }
150
151
        // Step 4: Initialize sound systems
152
        // Set up both the basic beeper and advanced AY-3-8912 sound chip
153
154
        // Initialize basic beeper sound system
155
1
        sound = std::make_unique<Sound>();
156
1
        if (!sound->initialize())
157
0
        {
158
0
            std::cerr << "Warning: Failed to initialize sound system" << std::endl;
159
            // Continue without sound if initialization fails
160
0
        }
161
162
        // Connect beeper to port 0xFE (shared with ULA)
163
1
        ports->RegisterWriteHandler(0xFE, [this](uint16_t port, uint8_t value)
164
717k
                                    { sound->writePort(port, value); });
165
166
        // Initialize AY8912 sound chip (provides better sound quality)
167
1
        ay8912 = std::make_unique<AY8912>();
168
1
        if (!ay8912->initialize())
169
0
        {
170
0
            std::cerr << "Warning: Failed to initialize AY8912 sound chip" << std::endl;
171
            // Continue without AY8912 sound if initialization fails
172
0
        }
173
174
        // Connect AY8912 to port 0xFD (shared with memory)
175
1
        ports->RegisterWriteHandler(0xFD, [this](uint16_t port, uint8_t value)
176
48.8k
                                    { ay8912->writePort(port, value); });
177
1
        ports->RegisterReadHandler(0xFD, [this](uint16_t port) -> uint8_t
178
96
                                   { return ay8912->readPort(port); });
179
180
        // Step 5: Create graphics window and rendering components
181
1
        window = SDL_CreateWindow("ZX Spectrum Emulator", 704, 576, SDL_WINDOW_RESIZABLE);
182
1
        if (window == nullptr)
183
0
        {
184
0
            std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
185
0
            return false;
186
0
        }
187
188
        // Show the window on screen
189
1
        SDL_ShowWindow(window);
190
191
        // Create renderer for drawing graphics
192
1
        renderer = SDL_CreateRenderer(window, nullptr);
193
1
        if (renderer == nullptr)
194
0
        {
195
0
            std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl;
196
0
            return false;
197
0
        }
198
199
        // Create texture for the screen (352x288 pixels matches the ULA output buffer)
200
1
        texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
201
1
                                    SDL_TEXTUREACCESS_STATIC, 352, 288);
202
1
        if (texture == nullptr)
203
0
        {
204
0
            std::cerr << "Texture could not be created! SDL_Error: " << SDL_GetError() << std::endl;
205
0
            return false;
206
0
        }
207
1
        SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
208
209
        // Step 6: Initialize ImGui for the user interface
210
1
        IMGUI_CHECKVERSION();   // Verify ImGui version compatibility
211
1
        ImGui::CreateContext(); // Create ImGui context
212
1
        ImGuiIO &io = ImGui::GetIO();
213
1
        (void)io;
214
1
        io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable keyboard navigation
215
216
        // Setup ImGui visual style
217
1
        ImGui::StyleColorsDark();
218
219
        // Setup platform/renderer backends for ImGui
220
1
        ImGui_ImplSDL3_InitForSDLRenderer(window, renderer);
221
1
        ImGui_ImplSDLRenderer3_Init(renderer);
222
223
        // Default to 48k Spectrum mode
224
1
        change48(true);
225
1
        return true;
226
1
    }
227
228
    void run()
229
1
    {
230
        // Event structure for handling SDL events (keyboard, mouse, window events, etc.)
231
1
        SDL_Event e;
232
233
        // Start the CPU emulation in a separate thread
234
        // This allows the UI to remain responsive while the CPU emulation runs
235
1
        runZX();
236
237
        // Main application loop - continues until quit flag is set
238
80.5k
        while (!quit)
239
80.5k
        {
240
            // Handle all pending events in the queue
241
            // Events include keyboard presses, mouse movements, window resizing, etc.
242
82.2k
            while (SDL_PollEvent(&e) != 0)
243
1.63k
            {
244
                // Pass SDL events to ImGui for processing (menu interactions, etc.)
245
1.63k
                ImGui_ImplSDL3_ProcessEvent(&e);
246
247
                // Handle different types of events
248
1.63k
                if (e.type == SDL_EVENT_QUIT)
249
1
                {
250
                    // User closed the window - clean up and exit
251
1
                    ay8912->cleanup();
252
1
                    sound->cleanup();
253
1
                    std::cout << "Quit event received" << std::endl;
254
1
                    quit = true;
255
1
                }
256
1.63k
                else if (e.type == SDL_EVENT_KEY_DOWN)
257
1.33k
                {
258
                    // Key pressed down - handle keyboard input
259
1.33k
                    handleKeyDown(e.key.key);
260
1.33k
                }
261
302
                else if (e.type == SDL_EVENT_KEY_UP)
262
162
                {
263
                    // Key released - handle keyboard input
264
162
                    handleKeyUp(e.key.key);
265
162
                }
266
1.63k
            }
267
268
            // Start a new ImGui frame for UI rendering
269
80.5k
            ImGui_ImplSDLRenderer3_NewFrame();
270
80.5k
            ImGui_ImplSDL3_NewFrame();
271
80.5k
            ImGui::NewFrame();
272
273
            // Create the main menu bar at the top of the window
274
80.5k
            if (ImGui::BeginMainMenuBar())
275
80.5k
            {
276
                // File menu - for loading files and exiting
277
80.5k
                if (ImGui::BeginMenu("File"))
278
0
                {
279
                    // Option to open a ROM/BIN/Z80 file
280
0
                    if (ImGui::MenuItem("Open File", "Ctrl+O"))
281
0
                    {
282
                        // Configure and open file dialog for ROM files
283
0
                        IGFD::FileDialogConfig config;
284
0
                        config.path = "."; // Start in current directory
285
0
                        ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", "Choose File", ".rom,.bin,.z80", config);
286
0
                    }
287
288
                    // Exit option
289
0
                    if (ImGui::MenuItem("Exit", "Alt+F4"))
290
0
                    {
291
0
                        quit = true;
292
0
                    }
293
0
                    ImGui::EndMenu();
294
0
                }
295
296
                // Window menu - for changing display scale
297
80.5k
                if (ImGui::BeginMenu("Window"))
298
0
                {
299
                    // Scale options to change window size
300
0
                    if (ImGui::MenuItem("Scale 1x"))
301
0
                    {
302
                        // Set window size to original resolution (352x288)
303
0
                        SDL_SetWindowMinimumSize(window, 352, 288);
304
0
                        SDL_SetWindowSize(window, 352, 288);
305
0
                    }
306
0
                    if (ImGui::MenuItem("Scale 2x"))
307
0
                    {
308
                        // Double the window size (704x576)
309
0
                        SDL_SetWindowMinimumSize(window, 704, 576);
310
0
                        SDL_SetWindowSize(window, 704, 576);
311
0
                    }
312
0
                    if (ImGui::MenuItem("Scale 3x"))
313
0
                    {
314
                        // Triple the window size (1056x864)
315
0
                        SDL_SetWindowMinimumSize(window, 1056, 864);
316
0
                        SDL_SetWindowSize(window, 1056, 864);
317
0
                    }
318
0
                    ImGui::EndMenu();
319
0
                }
320
321
                // Tape menu - for loading and controlling tape files
322
80.5k
                if (ImGui::BeginMenu("Tape"))
323
660
                {
324
                    // Load a tape file (.TAP or .TZX format)
325
660
                    if (ImGui::MenuItem("Load"))
326
0
                    {
327
                        // Configure and open file dialog for tape files
328
0
                        IGFD::FileDialogConfig config;
329
0
                        config.path = "."; // Start in current directory
330
0
                        ImGuiFileDialog::Instance()->OpenDialog("ChooseTapeDlgKey", "Choose Tape File", ".TAP,.TZX,.tap,.tzx", config);
331
0
                    }
332
333
                    // Start playing the currently loaded tape
334
660
                    if (ImGui::MenuItem("Play"))
335
1
                    {
336
1
                        StartTape();
337
1
                    }
338
339
                    // Toggle turbo loading mode (faster tape loading)
340
660
                    if (ImGui::MenuItem("Turboload", nullptr, tape ? tape->isTapeTurbo : false))
341
0
                    {
342
0
                        if (tape)
343
0
                        {
344
0
                            tape->isTapeTurbo = !tape->isTapeTurbo;
345
0
                        }
346
0
                    }
347
660
                    ImGui::EndMenu();
348
660
                }
349
80.5k
                ImGui::EndMainMenuBar();
350
80.5k
            }
351
352
            // Display file dialog for ROM loading if it's open
353
80.5k
            if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey", ImGuiWindowFlags_NoCollapse, ImVec2(400, 300)))
354
0
            {
355
                // If user clicked OK in the dialog
356
0
                if (ImGuiFileDialog::Instance()->IsOk())
357
0
                {
358
                    // Get the selected file path
359
0
                    std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
360
0
                    std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
361
                    // TODO: Actually load the selected ROM file
362
0
                    std::cout << "Selected file: " << filePathName << std::endl;
363
0
                }
364
365
                // Close the file dialog
366
0
                ImGuiFileDialog::Instance()->Close();
367
0
            }
368
369
            // Display file dialog for tape loading if it's open
370
80.5k
            if (ImGuiFileDialog::Instance()->Display("ChooseTapeDlgKey", ImGuiWindowFlags_NoCollapse, ImVec2(400, 300)))
371
0
            {
372
                // If user clicked OK in the dialog
373
0
                if (ImGuiFileDialog::Instance()->IsOk())
374
0
                {
375
                    // Get the selected tape file path
376
0
                    std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
377
0
                    std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
378
379
                    // Load the selected tape file
380
0
                    std::cout << "Selected tape file: " << filePathName << std::endl;
381
0
                    if (tape)
382
0
                    {
383
0
                        tape->loadFile(filePathName);
384
0
                        tape->prepareBitStream();
385
0
                    }
386
0
                }
387
388
                // Close the file dialog
389
0
                ImGuiFileDialog::Instance()->Close();
390
0
            }
391
392
            // Check if the emulation thread has updated the screen
393
            // We need to synchronize access to shared data using a mutex
394
80.5k
            bool updateScreen = false;
395
80.5k
            {
396
                // Lock the mutex to safely check screen update flag
397
80.5k
                std::lock_guard<std::mutex> lock(screenMutex);
398
80.5k
                if (screenUpdated)
399
4.46k
                {
400
4.46k
                    updateScreen = true;
401
4.46k
                    screenUpdated = false; // Reset the flag
402
4.46k
                }
403
80.5k
            }
404
405
            // Update the screen texture if the emulation thread has rendered a new frame
406
80.5k
            if (updateScreen)
407
4.46k
            {
408
                // Get the screen buffer from the ULA (graphics chip)
409
                // This must be done on the main thread because OpenGL/DirectX isn't thread-safe
410
4.46k
                uint32_t *src = ula->getScreenBuffer();
411
412
                // Update the SDL texture with the new screen data
413
4.46k
                SDL_UpdateTexture(texture, nullptr, src, 352 * sizeof(uint32_t));
414
4.46k
            }
415
416
            // Get current window dimensions for scaling calculations
417
80.5k
            int windowWidth, windowHeight;
418
80.5k
            SDL_GetWindowSize(window, &windowWidth, &windowHeight);
419
420
            // Update ImGui with current display size
421
80.5k
            ImGuiIO &io = ImGui::GetIO();
422
80.5k
            io.DisplaySize = ImVec2((float)windowWidth, (float)windowHeight);
423
424
            // Clear the screen with black to prevent flickering between frames
425
80.5k
            SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
426
80.5k
            SDL_RenderClear(renderer);
427
428
            // Calculate scaling to maintain correct aspect ratio
429
80.5k
            float scaleX = (float)windowWidth / 352.0f;  // Original width
430
80.5k
            float scaleY = (float)windowHeight / 288.0f; // Original height
431
80.5k
            float scale = std::min(scaleX, scaleY);      // Use the smaller scale to fit entirely
432
433
            // Calculate destination rectangle for scaled rendering
434
80.5k
            int destWidth = (int)(352.0f * scale);
435
80.5k
            int destHeight = (int)(288.0f * scale);
436
80.5k
            int destX = (windowWidth - destWidth) / 2;   // Center horizontally
437
80.5k
            int destY = (windowHeight - destHeight) / 2; // Center vertically
438
439
            // Create rectangle structure for rendering
440
80.5k
            SDL_FRect destRect = {(float)destX, (float)destY, (float)destWidth, (float)destHeight};
441
442
            // Render the scaled texture to the screen
443
80.5k
            SDL_RenderTexture(renderer, texture, nullptr, &destRect);
444
445
            // Render ImGui elements (menus, dialogs, etc.)
446
80.5k
            ImGui::Render();
447
80.5k
            ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
448
449
            // Present the final rendered frame to the screen
450
80.5k
            SDL_RenderPresent(renderer);
451
80.5k
        }
452
1
    }
453
454
    void cleanup()
455
1
    {
456
1
        sound->cleanup();
457
        // Stop the thread if it's running
458
1
        if (threadRunning.load())
459
1
        {
460
1
            threadRunning = false;
461
1
            if (emulationThread.joinable())
462
1
            {
463
1
                emulationThread.join();
464
1
            }
465
1
        }
466
467
        // Cleanup ImGui
468
1
        ImGui_ImplSDLRenderer3_Shutdown();
469
1
        ImGui_ImplSDL3_Shutdown();
470
1
        ImGui::DestroyContext();
471
472
        // Cleanup SDL resources
473
1
        if (texture)
474
1
        {
475
1
            SDL_DestroyTexture(texture);
476
1
            texture = nullptr;
477
1
        }
478
479
1
        if (renderer)
480
1
        {
481
1
            SDL_DestroyRenderer(renderer);
482
1
            renderer = nullptr;
483
1
        }
484
485
1
        if (window)
486
1
        {
487
1
            SDL_DestroyWindow(window);
488
1
            window = nullptr;
489
1
        }
490
491
1
        SDL_Quit();
492
1
    }
493
};
494
495
void Emulator::change48(bool is48)
496
1
{
497
1
    if (is48)
498
1
    {
499
1
        TARGET_FREQUENCY = 3500000;                // 3.5 MHz
500
1
        CHECK_INTERVAL = TARGET_FREQUENCY / 10000; // Check timing every 0.1ms (more precise)
501
1
        ula->change48(true);
502
1
    }
503
0
    else
504
0
    {
505
0
        TARGET_FREQUENCY = 3546900;                // 3.54690 Mhz
506
0
        CHECK_INTERVAL = TARGET_FREQUENCY / 10000; // Check timing every 0.1ms (more precise)
507
0
        ula->change48(false);
508
0
    }
509
1
}
510
511
void Emulator::StartTape()
512
1
{
513
    // Empty function as requested
514
1
    std::cout << "StartTape() called" << std::endl;
515
516
1
    if (tape)
517
1
    {
518
        // Set the tape to played state
519
1
        tape->isTapePlayed = true;
520
1
        std::cout << "Tape playback started" << std::endl;
521
1
    }
522
1
}
523
524
bool Emulator::loadTapeFile(const std::string &filePath)
525
1
{
526
1
    if (!tape)
527
0
    {
528
0
        std::cerr << "Tape system not initialized" << std::endl;
529
0
        return false;
530
0
    }
531
532
1
    std::cout << "Loading tape file: " << filePath << std::endl;
533
1
    tape->loadFile(filePath);
534
1
    tape->prepareBitStream();
535
1
    std::cout << "Tape file loaded successfully" << std::endl;
536
1
    return true;
537
1
}
538
539
void Emulator::startTapePlayback()
540
0
{
541
0
    if (tape)
542
0
    {
543
0
        tape->isTapePlayed = true;
544
0
        std::cout << "Tape playback started automatically" << std::endl;
545
0
    }
546
0
}
547
548
void Emulator::runZX()
549
1
{
550
    // Signal that the emulation thread should be running
551
1
    threadRunning = true;
552
553
    // Initialize memory with 128K ROM (default mode)
554
    // The ZX Spectrum 128K had more memory and additional features compared to the 48K model
555
1
    memory->Read128();       // Load 128K ROM
556
1
    memory->change48(false); // Set memory mode to 128K
557
558
    // Set CPU to CMOS mode (more accurate for later Spectrums)
559
    // The Z80 CPU in later Spectrum models was a CMOS variant
560
1
    cpu->isNMOS = false;
561
562
    // Create a new thread to run the CPU emulation
563
    // This allows the UI to remain responsive while the CPU emulation runs
564
1
    emulationThread = std::thread([this]()
565
1
                                  {
566
                                      // Timing variables for maintaining accurate CPU speed
567
1
                                      auto startTime = std::chrono::high_resolution_clock::now();
568
1
                                      long long totalTicks = 0; // Total CPU cycles executed
569
1
                                      long long checkTicks = 0; // Cycles since last timing check
570
571
                                      // Track previous tape state to detect when turbo mode turns off
572
1
                                      bool prevTapePlayed = false;
573
1
                                      bool prevTapeTurbo = false;
574
575
                                      // Main emulation loop - runs until threadRunning is set to false
576
168M
                                      while (threadRunning.load())
577
168M
                                      {
578
                                          // Execute one instruction and get the number of CPU cycles it took
579
168M
                                          int ticks = cpu->ExecuteOneInstruction();
580
581
                                          // Update our cycle counters
582
168M
                                          totalTicks += ticks;
583
168M
                                          checkTicks += ticks;
584
585
                                          // Update sound system with current cycle count
586
                                          // This ensures audio stays synchronized with the CPU
587
168M
                                          sound->ticks = totalTicks;
588
589
                                          // Update screen for each CPU tick
590
                                          // The ULA (graphics chip) needs to be updated for each cycle
591
1.41G
                                          for (int i = 0; i < ticks; i++)
592
1.24G
                                          {
593
                                              // oneTick() returns 0 when the screen is fully drawn
594
1.24G
                                              int ref = ula->oneTick();
595
1.24G
                                              if (ref == 0)
596
17.8k
                                              {
597
                                                  // Screen has been updated - notify the main thread
598
17.8k
                                                  {
599
                                                      // Lock the mutex to safely update shared data
600
17.8k
                                                      std::lock_guard<std::mutex> lock(screenMutex);
601
602
                                                      // Rate limiting for screen updates during tape turbo mode
603
                                                      // This prevents the UI from being overwhelmed during fast tape loading
604
17.8k
                                                      if (!tape->isTapePlayed || !tape->isTapeTurbo)
605
4.33k
                                                      {
606
                                                          // Normal operation - update screen immediately
607
4.33k
                                                          screenUpdated = true;
608
4.33k
                                                      }
609
13.4k
                                                      else
610
13.4k
                                                      {
611
                                                          // Turbo mode - limit screen updates to prevent UI lag
612
13.4k
                                                          auto now = std::chrono::high_resolution_clock::now();
613
13.4k
                                                          auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - lastScreenUpdate);
614
13.4k
                                                          if (elapsed >= minScreenUpdateInterval)
615
146
                                                          {
616
146
                                                              screenUpdated = true;
617
146
                                                              lastScreenUpdate = now;
618
146
                                                          }
619
13.4k
                                                      }
620
621
                                                      // Signal that an interrupt should be triggered
622
                                                      // This is part of the ZX Spectrum's timing system
623
17.8k
                                                      cpu->InterruptPending = true;
624
17.8k
                                                  }
625
17.8k
                                              }
626
1.24G
                                          }
627
628
                                          // Detect transition from turbo mode to normal mode
629
                                          // When this happens, we need to reset our timing calculations
630
168M
                                          if ((prevTapePlayed != tape->isTapePlayed || prevTapeTurbo != tape->isTapeTurbo) &&
631
168M
                                              (!tape->isTapePlayed || !tape->isTapeTurbo))
632
2
                                          {
633
                                              // Reset timing when transitioning to normal mode
634
2
                                              startTime = std::chrono::high_resolution_clock::now();
635
2
                                              totalTicks = 0;
636
2
                                              checkTicks = 0;
637
2
                                              std::cout << "Speed limiter re-enabled after tape play" << std::endl;
638
2
                                          }
639
640
                                          // Update previous states for next iteration
641
168M
                                          prevTapePlayed = tape->isTapePlayed;
642
168M
                                          prevTapeTurbo = tape->isTapeTurbo;
643
644
                                          // Determine if we should apply speed limiting
645
                                          // Speed limiting is disabled during tape turbo mode for faster loading
646
168M
                                          bool shouldDisableLimiter = !tape->isTapePlayed || !tape->isTapeTurbo;
647
648
                                          // Apply speed limiting to maintain accurate CPU frequency
649
                                          // Without this, the emulator would run as fast as possible
650
168M
                                          if (shouldDisableLimiter)
651
39.6M
                                          {
652
                                              // Check timing periodically (every CHECK_INTERVAL cycles)
653
39.6M
                                              if (checkTicks >= CHECK_INTERVAL)
654
855k
                                              {
655
855k
                                                  checkTicks = 0;
656
657
                                                  // Calculate how much time has actually passed
658
855k
                                                  auto currentTime = std::chrono::high_resolution_clock::now();
659
855k
                                                  auto elapsedTime = std::chrono::duration_cast<std::chrono::nanoseconds>(currentTime - startTime).count();
660
661
                                                  // Calculate how much time should have passed for the number of cycles executed
662
855k
                                                  long long expectedTime = (totalTicks * 1000000000LL) / TARGET_FREQUENCY;
663
664
                                                  // If we're ahead of schedule, we need to wait
665
855k
                                                  if (expectedTime > elapsedTime)
666
770k
                                                  {
667
                                                      // Calculate how long to wait (in microseconds)
668
770k
                                                      long long sleepTime = (expectedTime - elapsedTime) / 1000;
669
670
770k
                                                      if (sleepTime > 1000)
671
0
                                                      {
672
                                                          // For longer delays, use sleep (less precise but CPU-friendly)
673
0
                                                          std::this_thread::sleep_for(std::chrono::microseconds(sleepTime - 500));
674
                                                          // Fine-tune with busy waiting for accuracy
675
0
                                                          while (std::chrono::duration_cast<std::chrono::nanoseconds>(
676
0
                                                                     std::chrono::high_resolution_clock::now() - startTime)
677
0
                                                                     .count() < expectedTime)
678
0
                                                          {
679
0
                                                              std::this_thread::yield();
680
0
                                                          }
681
0
                                                      }
682
770k
                                                      else if (sleepTime > 0)
683
770k
                                                      {
684
                                                          // For short delays, use busy waiting for precision
685
540M
                                                          while (std::chrono::duration_cast<std::chrono::nanoseconds>(
686
540M
                                                                     std::chrono::high_resolution_clock::now() - startTime)
687
540M
                                                                     .count() < expectedTime)
688
539M
                                                          {
689
539M
                                                              std::this_thread::yield();
690
539M
                                                          }
691
770k
                                                      }
692
770k
                                                  }
693
855k
                                              }
694
39.6M
                                          }
695
168M
                                      } // End of main emulation loop
696
1
                                  }); // End of thread creation
697
1
}
698
699
// Helper function to handle Kempston joystick events
700
void handleKempstonJoystick(SDL_Keycode key, bool pressed, std::unique_ptr<Kempston> &kempston)
701
1.49k
{
702
    // Only process if Kempston joystick is available
703
1.49k
    if (!kempston)
704
0
        return;
705
706
    // Map arrow keys and Alt to joystick directions and fire button
707
1.49k
    switch (key)
708
1.49k
    {
709
394
    case SDLK_UP:
710
394
        kempston->setUp(pressed);
711
394
        break;
712
313
    case SDLK_DOWN:
713
313
        kempston->setDown(pressed);
714
313
        break;
715
0
    case SDLK_LEFT:
716
0
        kempston->setLeft(pressed);
717
0
        break;
718
564
    case SDLK_RIGHT:
719
564
        kempston->setRight(pressed);
720
564
        break;
721
216
    case SDLK_LALT:
722
216
        kempston->setFire(pressed);
723
216
        break;
724
1.49k
    }
725
1.49k
}
726
727
// Helper function to map SDL keys to ZX Spectrum keyboard matrix rows and columns
728
void mapKeyToSpectrum(SDL_Keycode key, bool pressed, ULA *ula)
729
1.49k
{
730
    // This function maps PC keyboard keys to the ZX Spectrum's keyboard matrix
731
    // The ZX Spectrum keyboard is organized in a grid of rows and columns
732
    // Each key press connects a specific row and column
733
734
    // Row and column mappings for each key:
735
    // Row 0: CAPS SHIFT, Z, X, C, V
736
    // Row 1: A, S, D, F, G
737
    // Row 2: Q, W, E, R, T
738
    // Row 3: 1, 2, 3, 4, 5
739
    // Row 4: 0, 9, 8, 7, 6
740
    // Row 5: P, O, I, U, Y
741
    // Row 6: ENTER, L, K, J, H
742
    // Row 7: SPACE, SYM SHIFT, M, N, B
743
744
1.49k
    switch (key)
745
1.49k
    {
746
    // Row 0: CAPS SHIFT, Z, X, C, V
747
0
    case SDLK_LSHIFT:
748
0
        if (pressed)
749
0
            ula->setKeyDown(0, 0);
750
0
        else
751
0
            ula->setKeyUp(0, 0); // CAPS SHIFT
752
0
        break;
753
0
    case SDLK_Z:
754
0
        if (pressed)
755
0
            ula->setKeyDown(0, 1);
756
0
        else
757
0
            ula->setKeyUp(0, 1); // Z
758
0
        break;
759
0
    case SDLK_X:
760
0
        if (pressed)
761
0
            ula->setKeyDown(0, 2);
762
0
        else
763
0
            ula->setKeyUp(0, 2); // X
764
0
        break;
765
0
    case SDLK_C:
766
0
        if (pressed)
767
0
            ula->setKeyDown(0, 3);
768
0
        else
769
0
            ula->setKeyUp(0, 3); // C
770
0
        break;
771
0
    case SDLK_V:
772
0
        if (pressed)
773
0
            ula->setKeyDown(0, 4);
774
0
        else
775
0
            ula->setKeyUp(0, 4); // V
776
0
        break;
777
778
    // Row 1: A, S, D, F, G
779
0
    case SDLK_A:
780
0
        if (pressed)
781
0
            ula->setKeyDown(1, 0);
782
0
        else
783
0
            ula->setKeyUp(1, 0); // A
784
0
        break;
785
0
    case SDLK_S:
786
0
        if (pressed)
787
0
            ula->setKeyDown(1, 1);
788
0
        else
789
0
            ula->setKeyUp(1, 1); // S
790
0
        break;
791
0
    case SDLK_D:
792
0
        if (pressed)
793
0
            ula->setKeyDown(1, 2);
794
0
        else
795
0
            ula->setKeyUp(1, 2); // D
796
0
        break;
797
0
    case SDLK_F:
798
0
        if (pressed)
799
0
            ula->setKeyDown(1, 3);
800
0
        else
801
0
            ula->setKeyUp(1, 3); // F
802
0
        break;
803
0
    case SDLK_G:
804
0
        if (pressed)
805
0
            ula->setKeyDown(1, 4);
806
0
        else
807
0
            ula->setKeyUp(1, 4); // G
808
0
        break;
809
810
    // Row 2: Q, W, E, R, T
811
0
    case SDLK_Q:
812
0
        if (pressed)
813
0
            ula->setKeyDown(2, 0);
814
0
        else
815
0
            ula->setKeyUp(2, 0); // Q
816
0
        break;
817
0
    case SDLK_W:
818
0
        if (pressed)
819
0
            ula->setKeyDown(2, 1);
820
0
        else
821
0
            ula->setKeyUp(2, 1); // W
822
0
        break;
823
0
    case SDLK_E:
824
0
        if (pressed)
825
0
            ula->setKeyDown(2, 2);
826
0
        else
827
0
            ula->setKeyUp(2, 2); // E
828
0
        break;
829
0
    case SDLK_R:
830
0
        if (pressed)
831
0
            ula->setKeyDown(2, 3);
832
0
        else
833
0
            ula->setKeyUp(2, 3); // R
834
0
        break;
835
0
    case SDLK_T:
836
0
        if (pressed)
837
0
            ula->setKeyDown(2, 4);
838
0
        else
839
0
            ula->setKeyUp(2, 4); // T
840
0
        break;
841
842
    // Row 3: 1, 2, 3, 4, 5
843
2
    case SDLK_1:
844
2
        if (pressed)
845
1
            ula->setKeyDown(3, 0);
846
1
        else
847
1
            ula->setKeyUp(3, 0); // 1
848
2
        break;
849
0
    case SDLK_2:
850
0
        if (pressed)
851
0
            ula->setKeyDown(3, 1);
852
0
        else
853
0
            ula->setKeyUp(3, 1); // 2
854
0
        break;
855
0
    case SDLK_3:
856
0
        if (pressed)
857
0
            ula->setKeyDown(3, 2);
858
0
        else
859
0
            ula->setKeyUp(3, 2); // 3
860
0
        break;
861
0
    case SDLK_4:
862
0
        if (pressed)
863
0
            ula->setKeyDown(3, 3);
864
0
        else
865
0
            ula->setKeyUp(3, 3); // 4
866
0
        break;
867
2
    case SDLK_5:
868
2
        if (pressed)
869
1
            ula->setKeyDown(3, 4);
870
1
        else
871
1
            ula->setKeyUp(3, 4); // 5
872
2
        break;
873
874
    // Row 4: 0, 9, 8, 7, 6
875
0
    case SDLK_0:
876
0
        if (pressed)
877
0
            ula->setKeyDown(4, 0);
878
0
        else
879
0
            ula->setKeyUp(4, 0); // 0
880
0
        break;
881
0
    case SDLK_9:
882
0
        if (pressed)
883
0
            ula->setKeyDown(4, 1);
884
0
        else
885
0
            ula->setKeyUp(4, 1); // 9
886
0
        break;
887
0
    case SDLK_8:
888
0
        if (pressed)
889
0
            ula->setKeyDown(4, 2);
890
0
        else
891
0
            ula->setKeyUp(4, 2); // 8
892
0
        break;
893
0
    case SDLK_7:
894
0
        if (pressed)
895
0
            ula->setKeyDown(4, 3);
896
0
        else
897
0
            ula->setKeyUp(4, 3); // 7
898
0
        break;
899
0
    case SDLK_6:
900
0
        if (pressed)
901
0
            ula->setKeyDown(4, 4);
902
0
        else
903
0
            ula->setKeyUp(4, 4); // 6
904
0
        break;
905
906
    // Row 5: P, O, I, U, Y
907
0
    case SDLK_P:
908
0
        if (pressed)
909
0
            ula->setKeyDown(5, 0);
910
0
        else
911
0
            ula->setKeyUp(5, 0); // P
912
0
        break;
913
0
    case SDLK_O:
914
0
        if (pressed)
915
0
            ula->setKeyDown(5, 1);
916
0
        else
917
0
            ula->setKeyUp(5, 1); // O
918
0
        break;
919
0
    case SDLK_I:
920
0
        if (pressed)
921
0
            ula->setKeyDown(5, 2);
922
0
        else
923
0
            ula->setKeyUp(5, 2); // I
924
0
        break;
925
0
    case SDLK_U:
926
0
        if (pressed)
927
0
            ula->setKeyDown(5, 3);
928
0
        else
929
0
            ula->setKeyUp(5, 3); // U
930
0
        break;
931
0
    case SDLK_Y:
932
0
        if (pressed)
933
0
            ula->setKeyDown(5, 4);
934
0
        else
935
0
            ula->setKeyUp(5, 4); // Y
936
0
        break;
937
938
    // Row 6: ENTER, L, K, J, H
939
2
    case SDLK_RETURN:
940
2
    case SDLK_KP_ENTER:
941
2
        if (pressed)
942
1
            ula->setKeyDown(6, 0);
943
1
        else
944
1
            ula->setKeyUp(6, 0); // ENTER
945
2
        break;
946
0
    case SDLK_L:
947
0
        if (pressed)
948
0
            ula->setKeyDown(6, 1);
949
0
        else
950
0
            ula->setKeyUp(6, 1); // L
951
0
        break;
952
0
    case SDLK_K:
953
0
        if (pressed)
954
0
            ula->setKeyDown(6, 2);
955
0
        else
956
0
            ula->setKeyUp(6, 2); // K
957
0
        break;
958
0
    case SDLK_J:
959
0
        if (pressed)
960
0
            ula->setKeyDown(6, 3);
961
0
        else
962
0
            ula->setKeyUp(6, 3); // J
963
0
        break;
964
0
    case SDLK_H:
965
0
        if (pressed)
966
0
            ula->setKeyDown(6, 4);
967
0
        else
968
0
            ula->setKeyUp(6, 4); // H
969
0
        break;
970
971
    // Row 7: SPACE, SYM SHIFT, M, N, B
972
0
    case SDLK_SPACE:
973
0
        if (pressed)
974
0
            ula->setKeyDown(7, 0);
975
0
        else
976
0
            ula->setKeyUp(7, 0); // SPACE
977
0
        break;
978
0
    case SDLK_RSHIFT:
979
0
        if (pressed)
980
0
            ula->setKeyDown(7, 1);
981
0
        else
982
0
            ula->setKeyUp(7, 1); // SYM SHIFT
983
0
        break;
984
0
    case SDLK_M:
985
0
        if (pressed)
986
0
            ula->setKeyDown(7, 2);
987
0
        else
988
0
            ula->setKeyUp(7, 2); // M
989
0
        break;
990
0
    case SDLK_N:
991
0
        if (pressed)
992
0
            ula->setKeyDown(7, 3);
993
0
        else
994
0
            ula->setKeyUp(7, 3); // N
995
0
        break;
996
0
    case SDLK_B:
997
0
        if (pressed)
998
0
            ula->setKeyDown(7, 4);
999
0
        else
1000
0
            ula->setKeyUp(7, 4); // B
1001
0
        break;
1002
1.49k
    }
1003
1.49k
}
1004
1005
// Handle key down events
1006
void Emulator::handleKeyDown(SDL_Keycode key)
1007
1.33k
{
1008
    // Handle Kempston joystick with arrow keys and Alt
1009
1.33k
    handleKempstonJoystick(key, true, kempston);
1010
1011
    // Map SDL keys to ZX Spectrum keyboard matrix
1012
1.33k
    mapKeyToSpectrum(key, true, ula.get());
1013
1.33k
}
1014
1015
// Handle key up events
1016
void Emulator::handleKeyUp(SDL_Keycode key)
1017
162
{
1018
    // Handle Kempston joystick with arrow keys and Alt
1019
162
    handleKempstonJoystick(key, false, kempston);
1020
1021
    // Map SDL keys to ZX Spectrum keyboard matrix
1022
162
    mapKeyToSpectrum(key, false, ula.get());
1023
162
}
1024
1025
// Main entry point of the program
1026
// This is where execution begins when you start the emulator
1027
int main(int argc, char *argv[])
1028
1
{
1029
    // Welcome message for users
1030
1
    std::cout << "ZX Spectrum Emulator starting..." << std::endl;
1031
1032
    // Create an instance of our Emulator class
1033
    // This will initialize all the components we need
1034
    // The constructor sets up default values, but doesn't allocate resources yet
1035
1
    Emulator emulator;
1036
1037
    // Try to initialize the emulator
1038
    // This sets up SDL, creates the window, loads ROMs, etc.
1039
    // If this fails, we can't continue with the program
1040
1
    std::cout << "Initializing emulator components..." << std::endl;
1041
1
    if (!emulator.initialize())
1042
0
    {
1043
0
        std::cerr << "Failed to initialize emulator!" << std::endl;
1044
0
        std::cerr << "The emulator could not start because of initialization errors." << std::endl;
1045
0
        return -1; // Return error code if initialization fails
1046
0
    }
1047
1
    std::cout << "Emulator initialized successfully!" << std::endl;
1048
1049
    // Check if a file path was provided as a command line argument
1050
    // This allows users to load a tape file directly when starting the emulator
1051
    // Usage: ./emulator myfile.tap
1052
1
    if (argc > 1)
1053
1
    {
1054
        // Get the file path from command line arguments
1055
        // argv[0] is the program name, argv[1] is the first argument
1056
1
        std::string filePath = argv[1];
1057
1
        std::cout << "Loading tape file from command line: " << filePath << std::endl;
1058
1059
        // Load the tape file and prepare it for playback
1060
        // This reads the file into memory and prepares it for the emulator to use
1061
1
        if (emulator.loadTapeFile(filePath))
1062
1
        {
1063
1
            std::cout << "Tape file loaded successfully. Use Tape->Play menu to start playback." << std::endl;
1064
1
        }
1065
0
        else
1066
0
        {
1067
0
            std::cerr << "Failed to load tape file: " << filePath << std::endl;
1068
0
            std::cerr << "The emulator will continue without the tape file." << std::endl;
1069
0
        }
1070
1
    }
1071
0
    else
1072
0
    {
1073
0
        std::cout << "No tape file specified. You can load one using the Tape->Load menu." << std::endl;
1074
0
    }
1075
1076
    // Start the main emulator loop
1077
    // This will run until the user closes the window or exits the program
1078
    // The run() method contains the main application loop that handles:
1079
    // - Processing user input (keyboard, mouse, menus)
1080
    // - Updating the display with the emulated screen
1081
    // - Managing the emulation thread that runs the Z80 CPU
1082
1
    std::cout << "Starting emulator loop..." << std::endl;
1083
1
    emulator.run();
1084
1085
    // When run() finishes (user closed the window), the program ends
1086
1
    std::cout << "Emulator shutting down..." << std::endl;
1087
1088
    // Program ends successfully
1089
1
    std::cout << "Emulator exited normally." << std::endl;
1090
1
    return 0;
1091
1
}
/Users/kiltum/projects/zxcpp/src/kempston.cpp
Line
Count
Source
1
#include "kempston.hpp"
2
#include <iostream>
3
4
// Constructor
5
Kempston::Kempston()
6
1
{
7
    // Initialize joystick state (all buttons released)
8
1
    joystickState = 0x00;
9
1
}
10
11
// Destructor
12
Kempston::~Kempston()
13
1
{
14
    // Nothing to clean up
15
1
}
16
17
// Read a byte from the specified port
18
uint8_t Kempston::readPort(uint16_t port)
19
1.51k
{
20
    // Kempston joystick handles port 0x1F for joystick input
21
1.51k
    if ((port & 0xFF) == 0x1F)
22
1.51k
    {
23
        // Return the current joystick state
24
        // Bits 0-4: Right, Left, Down, Up, Fire
25
        // Bits 5-7: Reserved (should be 0)
26
1.51k
        return joystickState & 0x1F;
27
1.51k
    }
28
29
0
    return 0;
30
1.51k
}
31
32
// Set the right direction button state
33
void Kempston::setRight(bool pressed)
34
564
{
35
564
    if (pressed)
36
551
    {
37
551
        joystickState |= 0x01; // Set bit 0
38
551
    }
39
13
    else
40
13
    {
41
13
        joystickState &= ~0x01; // Clear bit 0
42
13
    }
43
564
}
44
45
// Set the left direction button state
46
void Kempston::setLeft(bool pressed)
47
0
{
48
0
    if (pressed)
49
0
    {
50
0
        joystickState |= 0x02; // Set bit 1
51
0
    }
52
0
    else
53
0
    {
54
0
        joystickState &= ~0x02; // Clear bit 1
55
0
    }
56
0
}
57
58
// Set the down direction button state
59
void Kempston::setDown(bool pressed)
60
313
{
61
313
    if (pressed)
62
298
    {
63
298
        joystickState |= 0x04; // Set bit 2
64
298
    }
65
15
    else
66
15
    {
67
15
        joystickState &= ~0x04; // Clear bit 2
68
15
    }
69
313
}
70
71
// Set the up direction button state
72
void Kempston::setUp(bool pressed)
73
394
{
74
394
    if (pressed)
75
371
    {
76
371
        joystickState |= 0x08; // Set bit 3
77
371
    }
78
23
    else
79
23
    {
80
23
        joystickState &= ~0x08; // Clear bit 3
81
23
    }
82
394
}
83
84
// Set the fire button state
85
void Kempston::setFire(bool pressed)
86
216
{
87
216
    if (pressed)
88
108
    {
89
108
        joystickState |= 0x10; // Set bit 4
90
108
    }
91
108
    else
92
108
    {
93
108
        joystickState &= ~0x10; // Clear bit 4
94
108
    }
95
216
}
96
97
// Reset joystick state
98
void Kempston::reset()
99
0
{
100
0
    joystickState = 0x00; // All buttons released
101
0
}
/Users/kiltum/projects/zxcpp/src/memory.cpp
Line
Count
Source
1
#include <iostream>
2
3
#include "memory.hpp"
4
// generated by xxd -i ...
5
#include "48rom.h"
6
#include "diagrom.h"
7
#include "diagrom2.h"
8
#include "1280.h"
9
#include "1281.h"
10
11
Memory::Memory()
12
1
{
13
    // clear all 128k memory
14
9
    for (int i = 0; i < 8; i++)
15
8
    {
16
131k
        for (int b = 0; b < 16384; b++)
17
131k
        {
18
131k
            bank[i][b] = 0x00;
19
131k
        }
20
8
    }
21
1
    canWriteRom = false;
22
1
    is48 = true;
23
1
    bankMapping[0] = 0; // 0 ROM - specially, mapped to 0x0000-0x3fff
24
1
    bankMapping[1] = 5; // bank 5 mapped to 0x4000-0x7fff
25
1
    bankMapping[2] = 2; // bank 2 always mapped to 0x8000-0xbfff
26
1
    bankMapping[3] = 0; // bank 0 mapped 0xc000-0xffff
27
1
    ULAShadow = false;  // ULA reading from bank 5 (false) or bank 7 (true)
28
1
}
29
30
void Memory::writePort(uint16_t port, uint8_t value)
31
48.8k
{
32
48.8k
    if (port == 0x7ffd)
33
1.34k
    {
34
1.34k
        if (is48)
35
0
        {
36
0
            printf("Memory: We are in 48k, so ignoring write to 7ffd\n");
37
0
        }
38
1.34k
        else
39
1.34k
        {
40
1.34k
            bankMapping[3] = value & 0x07;
41
1.34k
            ULAShadow = (value & 0x08) ? true : false;
42
1.34k
            bankMapping[0] = (value & 0x10) ? 1 : 0;
43
1.34k
            is48 = (value & 0x20) ? true : false; // disable future using of this port
44
            // printf("%x -> %x bank %d shadow %d rom %d\n", port, value, bankMapping[3], ULAShadow, bankMapping[0]);
45
1.34k
        }
46
1.34k
    }
47
48.8k
}
48
49
uint8_t Memory::ReadByte(uint16_t address)
50
432M
{
51
432M
    if (address <= 0x3fff) // ROM 0 or ROM 1
52
33.3M
    {
53
33.3M
        return rom[bankMapping[0]][address];
54
33.3M
    }
55
399M
    if (address >= 0x4000 && address < 0x8000)
56
41.8M
    {
57
41.8M
        return bank[bankMapping[1]][address - 0x4000];
58
41.8M
    }
59
357M
    if (address >= 0x8000 && address < 0xc000)
60
51.0M
    {
61
51.0M
        return bank[bankMapping[2]][address - 0x8000];
62
51.0M
    }
63
    // address >= 0xc000
64
306M
    return bank[bankMapping[3]][address - 0xc000];
65
357M
}
66
67
uint8_t Memory::ULAReadByte(uint16_t address)
68
1.75G
{
69
1.75G
    if (ULAShadow) // read from shadow
70
0
    {
71
0
        return bank[7][address - 0x4000];
72
0
    }
73
1.75G
    else
74
1.75G
    {
75
1.75G
        return bank[5][address - 0x4000];
76
1.75G
    }
77
1.75G
}
78
79
void Memory::WriteByte(uint16_t address, uint8_t value)
80
12.7M
{
81
12.7M
    if (address < 0x4000 && canWriteRom == false)
82
756k
    {
83
        // printf("Ignoring attempt to write byte to ROM %x %x\n",address,value);
84
756k
        return;
85
756k
    }
86
12.0M
    if (address <= 0x3fff) // ROM 0 or ROM 1
87
32.7k
    {
88
32.7k
        rom[bankMapping[0]][address] = value;
89
32.7k
    }
90
12.0M
    else if (address >= 0x4000 && address < 0x8000)
91
7.54M
    {
92
7.54M
        bank[bankMapping[1]][address - 0x4000] = value;
93
7.54M
    }
94
4.46M
    else if (address >= 0x8000 && address < 0xc000)
95
467k
    {
96
467k
        bank[bankMapping[2]][address - 0x8000] = value;
97
467k
    }
98
3.99M
    else // address >= 0xc000
99
3.99M
    {
100
3.99M
        bank[bankMapping[3]][address - 0xc000] = value;
101
3.99M
    }
102
12.0M
}
103
104
void Memory::change48(bool is48s)
105
1
{
106
1
    is48 = is48s;
107
1
}
108
109
void Memory::Read48(void)
110
0
{
111
0
    canWriteRom = true;
112
0
    for (unsigned int i = 0; i < __48_rom_len; i++)
113
0
    {
114
0
        WriteByte(i, __48_rom[i]);
115
0
    }
116
0
    canWriteRom = false;
117
0
}
118
119
void Memory::Read128(void)
120
1
{
121
1
    canWriteRom = true;
122
1
    bankMapping[0] = 0;
123
16.3k
    for (unsigned int i = 0; i < __128_0_rom_len; i++)
124
16.3k
    {
125
16.3k
        WriteByte(i, __128_0_rom[i]);
126
16.3k
    }
127
1
    bankMapping[0] = 1;
128
16.3k
    for (unsigned int i = 0; i < __128_1_rom_len; i++)
129
16.3k
    {
130
16.3k
        WriteByte(i, __128_1_rom[i]);
131
16.3k
    }
132
1
    bankMapping[0] = 0;
133
1
    canWriteRom = false;
134
1
}
135
136
void Memory::ReadDiag(void)
137
0
{
138
0
    canWriteRom = true;
139
0
    bankMapping[0] = 0;
140
0
    for (unsigned int i = 0; i < testrom_bin_len; i++)
141
0
    {
142
0
        WriteByte(i, testrom_bin[i]);
143
0
    }
144
0
    canWriteRom = false;
145
0
}
146
147
void Memory::ReadDiag2(void)
148
0
{
149
0
    canWriteRom = true;
150
0
    bankMapping[0] = 0;
151
0
    for (unsigned int i = 0; i < DiagROMv_173_len; i++)
152
0
    {
153
0
        WriteByte(i, DiagROMv_173[i]);
154
0
    }
155
0
    canWriteRom = false;
156
0
}
/Users/kiltum/projects/zxcpp/src/port.cpp
Line
Count
Source
1
#include "port.hpp"
2
#include <stdexcept>
3
4
Port::Port()
5
1
{
6
    // Constructor implementation
7
1
}
8
9
void Port::RegisterWriteHandler(uint16_t port, WriteHandler handler)
10
4
{
11
    // Add the handler to the vector for this port
12
4
    writeHandlers[port].push_back(handler);
13
4
}
14
15
void Port::RegisterReadHandler(uint16_t port, ReadHandler handler)
16
3
{
17
    // Check if a read handler is already registered for this port
18
3
    if (readHandlers.find(port) != readHandlers.end())
19
0
    {
20
        // Optionally throw an exception or overwrite - here we'll overwrite
21
0
    }
22
23
    // Register the handler for this port
24
3
    readHandlers[port] = handler;
25
3
}
26
27
void Port::Write(uint16_t port, uint8_t value)
28
766k
{
29
    // For ZX Spectrum compatibility, try masked port addressing
30
    // For port 0xFE, also check addresses like 0xFFFE, 0xFDFE, etc.
31
766k
    uint8_t lowByte = port & 0xFF;
32
766k
    auto maskedIt = writeHandlers.find(lowByte);
33
766k
    if (maskedIt != writeHandlers.end())
34
766k
    {
35
        // Call all registered handlers for the masked port
36
766k
        for (const auto &handler : maskedIt->second)
37
1.53M
        {
38
1.53M
            handler(port, value);
39
1.53M
        }
40
766k
    }
41
0
    else
42
0
        printf("Handler for writing %x to %x port not found\n", value, port);
43
766k
}
44
45
uint8_t Port::Read(uint16_t port)
46
9.95M
{
47
    // For ZX Spectrum compatibility, try masked port addressing
48
9.95M
    uint8_t lowByte = port & 0xFF;
49
9.95M
    auto maskedIt = readHandlers.find(lowByte);
50
9.95M
    if (maskedIt != readHandlers.end())
51
9.95M
    {
52
        // Call the registered handler for the masked port and return its result
53
9.95M
        return maskedIt->second(port);
54
9.95M
    }
55
56
    // Return 0 if no handler is registered for this port
57
0
    return uint8_t(port >> 8); // this is for FUSE test and Floating bus test
58
9.95M
}
/Users/kiltum/projects/zxcpp/src/sound.cpp
Line
Count
Source
1
#include "sound.hpp"
2
#include <iostream>
3
#include <cstring>
4
#include <vector>
5
#include <algorithm>
6
#include <cmath>
7
8
1
Sound::Sound() : audioStream(nullptr), audioDevice(0), initialized(false)
9
1
{
10
1
}
11
12
Sound::~Sound()
13
1
{
14
1
    cleanup();
15
1
}
16
17
bool Sound::initialize()
18
1
{
19
    // Check if audio subsystem is available
20
1
    if (!(SDL_WasInit(SDL_INIT_AUDIO) & SDL_INIT_AUDIO))
21
0
    {
22
0
        std::cerr << "SDL audio subsystem not initialized, skipping sound initialization" << std::endl;
23
0
        return false;
24
0
    }
25
26
1
    SDL_AudioSpec desired_spec;
27
1
    SDL_zero(desired_spec);
28
1
    desired_spec.format = SDL_AUDIO_S16;
29
1
    desired_spec.freq = 44100;
30
1
    desired_spec.channels = 2;
31
32
1
    audioStream = SDL_CreateAudioStream(&desired_spec, &desired_spec);
33
1
    if (!audioStream)
34
0
    {
35
0
        printf("Error creating audio stream: %s\n", SDL_GetError());
36
0
        return false;
37
0
    }
38
39
    // Create and open audio device, then bind the stream to it
40
1
    audioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &desired_spec);
41
1
    if (!audioDevice)
42
0
    {
43
0
        printf("Error opening audio device: %s\n", SDL_GetError());
44
0
        SDL_DestroyAudioStream(audioStream);
45
0
        audioStream = nullptr;
46
0
        return false;
47
0
    }
48
49
    // Bind the audio stream to the device
50
1
    if (!SDL_BindAudioStream(audioDevice, audioStream))
51
0
    {
52
0
        printf("Error binding audio stream: %s\n", SDL_GetError());
53
0
        SDL_CloseAudioDevice(audioDevice);
54
0
        SDL_DestroyAudioStream(audioStream);
55
0
        audioStream = nullptr;
56
0
        audioDevice = 0;
57
0
        return false;
58
0
    }
59
60
    // Start audio playback
61
1
    SDL_ResumeAudioDevice(audioDevice);
62
63
1
    initialized = true;
64
1
    ticks = 0;
65
1
    ticksPassed = 0;
66
1
    std::cout << "Sound system initialized successfully" << std::endl;
67
1
    return true;
68
1
}
69
70
void Sound::cleanup()
71
3
{
72
    // // Close the audio device first, which should automatically unbind any streams
73
3
    if (audioDevice)
74
1
    {
75
1
        SDL_CloseAudioDevice(audioDevice);
76
1
        audioDevice = 0;
77
1
    }
78
79
    // Then destroy the audio stream
80
3
    if (audioStream)
81
1
    {
82
1
        SDL_DestroyAudioStream(audioStream);
83
1
        audioStream = nullptr;
84
1
    }
85
86
3
    initialized = false;
87
3
}
88
89
void Sound::writePort(uint16_t port, uint8_t value)
90
717k
{
91
717k
    if ((port & 0xFF) == 0xFE)
92
717k
    {
93
717k
        bool micBit = (value & 0x08) == 0; // MIC is bit 3 (0x08) - active low
94
717k
        bool earBit = (value & 0x10) != 0; // EAR is bit 4 (0x10) - active high
95
96
        // prevent flooding of sound subsytem when border changes rapidly, but no sound output. Like when tape loads
97
717k
        if (micBit == lastMicBit && earBit == lastEarBit)
98
717k
            return;
99
100
14
        lastEarBit = earBit;
101
14
        lastMicBit = micBit;
102
14
        unsigned int duration = ticks - ticksPassed;
103
14
        ticksPassed = ticks;
104
        // printf("%lld %d %d\n", ticks - ticksPassed, earBit, micBit);
105
106
14
        if (duration < 1000000) // 3 500 000 per sec , 1000000 is ~0.3 sec.
107
5
        {
108
5
            if (earBit)
109
0
            { // Volume on speaker or tape raised to up
110
0
                generateAudio(duration, true);
111
                // SDL_FlushAudioStream(audioStream);
112
                // printf("%d 1\n",duration);
113
0
            }
114
5
            else
115
5
            {
116
5
                generateAudio(duration, false);
117
                // SDL_FlushAudioStream(audioStream);
118
                // printf("%d 0\n",duration);
119
5
            }
120
5
        }
121
9
        else
122
9
        {
123
            // printf("%lld Skipped %u\n", ticks,duration);
124
9
        }
125
14
    }
126
717k
}
127
128
void Sound::generateAudio(long long ticks, bool value)
129
5
{
130
5
    if (!initialized || !audioStream)
131
0
    {
132
0
        return;
133
0
    }
134
135
    // Calculate duration in seconds
136
    // 1 tick = 1/3500000 seconds
137
5
    double duration = static_cast<double>(ticks) / 3500000.0;
138
139
    // Calculate number of samples (assuming 44100 Hz sample rate)
140
5
    int sampleRate = 44100;
141
    // Use proper rounding instead of truncation + arbitrary offset
142
5
    int numSamples = static_cast<int>(std::round(duration * sampleRate));
143
144
    // numSamples = numSamples - 10; // Lets try to pitch up tone
145
    //  If no samples to generate, return early
146
5
    if (numSamples <= 0)
147
0
    {
148
0
        return;
149
0
    }
150
151
    // Create buffer for samples (16-bit stereo)
152
    // Allocate memory for stereo samples (2 channels)
153
5
    std::vector<int16_t> buffer(numSamples * 2);
154
155
    // Generate samples based on speaker membrane position
156
5
    int16_t sampleValue = value ? 10000 : -10000; // Amplitude for up/down position
157
158
    // Fill buffer with samples
159
4.92k
    for (int i = 0; i < numSamples; i++)
160
4.91k
    {
161
        // Stereo - same value for both channels
162
4.91k
        buffer[i * 2] = sampleValue;     // Left channel
163
4.91k
        buffer[i * 2 + 1] = sampleValue; // Right channel
164
4.91k
    }
165
166
    // Put audio data into the stream
167
5
    if (!SDL_PutAudioStreamData(audioStream, buffer.data(), numSamples * 2 * sizeof(int16_t)))
168
0
    {
169
0
        printf("Put audio failed: %s\n", SDL_GetError());
170
0
    }
171
5
}
/Users/kiltum/projects/zxcpp/src/tape.cpp
Line
Count
Source
1
#include "tape.hpp"
2
#include <iostream>
3
#include <fstream>
4
#include <cstring>
5
#include <algorithm>
6
#include <string>
7
#include <zip.h>
8
9
// Constructor
10
// Initializes the tape object with default values by calling reset()
11
Tape::Tape()
12
1
{
13
1
    reset();
14
1
}
15
16
// Destructor
17
Tape::~Tape()
18
1
{
19
    // Nothing to clean up
20
1
}
21
22
// Reset tape state
23
// This function initializes all tape-related variables to their default values
24
void Tape::reset()
25
1
{
26
    // State flags
27
1
    isTapePlayed = false; // Tape is not currently playing
28
1
    isTapeTurbo = true;   // Initialize turboload mode to true (faster loading)
29
30
    // Clear all data containers
31
1
    tapeData.clear();  // Raw tape data from file
32
1
    tapBlocks.clear(); // Parsed blocks from TAP/TZX files
33
1
    bitStream.clear(); // Generated bit stream for playback
34
35
    // Reset playback position counters
36
1
    currentImpulseIndex = 0; // Index of current impulse in bit stream
37
1
    currentImpulseTicks = 0; // Tick counter within current impulse
38
39
    // ZX Spectrum tape timing parameters (in CPU ticks)
40
1
    tapePilotLenHeader = 3000; // Number of pilot pulses for header blocks
41
1
    tapePilotLenData = 3223;   // Number of pilot pulses for data blocks
42
1
    tapePilot = 2168;          // Duration of each pilot pulse
43
1
    tapePilotPause = 3500000;  // Duration of pause between blocks
44
1
    tape0 = 855;               // Duration of pulse for bit 0
45
1
    tape1 = 1710;              // Duration of pulse for bit 1
46
1
    tapeSync1 = 667;           // Duration of first sync pulse
47
1
    tapeSync2 = 735;           // Duration of second sync pulse
48
1
    tapeFinalSync = 945;       // Duration of final sync pulse
49
1
}
50
51
// Helper function to check if a string ends with a specific suffix
52
// This is useful for checking file extensions
53
// Parameters:
54
//   str: the string to check
55
//   suffix: the suffix to look for
56
// Returns: true if str ends with suffix, false otherwise
57
bool endsWith(const std::string &str, const std::string &suffix)
58
3
{
59
    // If the suffix is longer than the string, it can't match
60
3
    if (suffix.length() > str.length())
61
0
    {
62
0
        return false;
63
0
    }
64
    // Compare the end of the string with the suffix
65
    // rbegin()/rend() iterate backwards from the end
66
3
    return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
67
3
}
68
69
// Helper function to validate checksum
70
// This function checks if the last byte of a block is a valid checksum
71
// The checksum is calculated by XORing all bytes except the last one
72
// Parameters:
73
//   blockData: vector containing the block data (including checksum)
74
// Returns: true if checksum is valid, false otherwise
75
bool Tape::validateChecksum(const std::vector<uint8_t> &blockData)
76
8
{
77
    // Need at least 2 bytes (data + checksum)
78
8
    if (blockData.size() < 2)
79
0
    {
80
0
        return false;
81
0
    }
82
83
    // Calculate checksum by XORing all bytes except the last one
84
8
    uint8_t calculatedChecksum = 0;
85
42.5k
    for (size_t i = 0; i < blockData.size() - 1; i++)
86
42.5k
    {
87
42.5k
        calculatedChecksum ^= blockData[i];
88
42.5k
    }
89
90
    // Compare calculated checksum with the stored one (last byte)
91
8
    return calculatedChecksum == blockData.back();
92
8
}
93
94
// Helper function to parse header information
95
void Tape::parseHeaderInfo(TapBlock &block)
96
4
{
97
4
    if (block.flag != 0x00 || block.data.size() < 17)
98
0
    {
99
        // Not a header block or insufficient data
100
0
        return;
101
0
    }
102
103
    // Parse header fields
104
4
    block.fileType = block.data[0];
105
106
    // Extract filename (10 characters)
107
4
    block.filename = std::string(reinterpret_cast<const char *>(block.data.data() + 1), 10);
108
109
    // Extract data length (2 bytes, little-endian)
110
4
    block.dataLength = static_cast<uint16_t>(block.data[11]) |
111
4
                       (static_cast<uint16_t>(block.data[12]) << 8);
112
113
    // Extract parameters (2 bytes each, little-endian)
114
4
    block.param1 = static_cast<uint16_t>(block.data[13]) |
115
4
                   (static_cast<uint16_t>(block.data[14]) << 8);
116
117
4
    block.param2 = static_cast<uint16_t>(block.data[15]) |
118
4
                   (static_cast<uint16_t>(block.data[16]) << 8);
119
4
}
120
121
// Load tape file
122
bool Tape::loadFile(const std::string &fileName)
123
1
{
124
    // Convert filename to lowercase for comparison
125
1
    std::string lowerFileName = fileName;
126
1
    std::transform(lowerFileName.begin(), lowerFileName.end(), lowerFileName.begin(), ::tolower);
127
128
    // Check if file is zipped
129
1
    if (endsWith(lowerFileName, ".zip"))
130
1
    {
131
1
        std::cout << "ZIP file detected: " << fileName << std::endl;
132
133
        // Open ZIP archive
134
1
        int err = 0;
135
1
        zip_t *archive = zip_open(fileName.c_str(), 0, &err);
136
1
        if (!archive)
137
0
        {
138
0
            std::cerr << "Failed to open ZIP file: " << fileName << " (error: " << err << ")" << std::endl;
139
0
            return false;
140
0
        }
141
142
        // Get the number of entries in the ZIP
143
1
        zip_int64_t num_entries = zip_get_num_entries(archive, 0);
144
1
        if (num_entries <= 0)
145
0
        {
146
0
            std::cerr << "ZIP file is empty: " << fileName << std::endl;
147
0
            zip_close(archive);
148
0
            return false;
149
0
        }
150
151
        // Look for the first file with .tap or .tzx extension
152
1
        zip_int64_t target_index = -1;
153
1
        std::string target_filename;
154
1
        bool is_tap = false;
155
156
1
        for (zip_int64_t i = 0; i < num_entries; i++)
157
1
        {
158
1
            const char *entry_name = zip_get_name(archive, i, 0);
159
1
            if (!entry_name)
160
0
                continue;
161
162
1
            std::string entry_name_str(entry_name);
163
1
            std::transform(entry_name_str.begin(), entry_name_str.end(), entry_name_str.begin(), ::tolower);
164
165
1
            if (endsWith(entry_name_str, ".tap"))
166
0
            {
167
0
                target_index = i;
168
0
                target_filename = entry_name;
169
0
                is_tap = true;
170
0
                break;
171
0
            }
172
1
            else if (endsWith(entry_name_str, ".tzx"))
173
1
            {
174
1
                target_index = i;
175
1
                target_filename = entry_name;
176
1
                is_tap = false;
177
1
                break;
178
1
            }
179
1
        }
180
181
1
        if (target_index == -1)
182
0
        {
183
0
            std::cerr << "No supported tape file (.tap or .tzx) found in ZIP: " << fileName << std::endl;
184
0
            zip_close(archive);
185
0
            return false;
186
0
        }
187
188
        // Open the target file from ZIP
189
1
        zip_file_t *file = zip_fopen_index(archive, target_index, 0);
190
1
        if (!file)
191
0
        {
192
0
            std::cerr << "Failed to open file from ZIP: " << target_filename << std::endl;
193
0
            zip_close(archive);
194
0
            return false;
195
0
        }
196
197
        // Read the file data
198
1
        std::vector<uint8_t> data;
199
1
        char buffer[8192];
200
1
        zip_int64_t bytes_read;
201
202
7
        while ((bytes_read = zip_fread(file, buffer, sizeof(buffer))) > 0)
203
6
        {
204
6
            data.insert(data.end(), buffer, buffer + bytes_read);
205
6
        }
206
207
        // Close the file and archive
208
1
        zip_fclose(file);
209
1
        zip_close(archive);
210
211
1
        if (bytes_read < 0)
212
0
        {
213
0
            std::cerr << "Error reading file from ZIP: " << target_filename << std::endl;
214
0
            return false;
215
0
        }
216
217
1
        std::cout << "Extracted " << data.size() << " bytes from " << target_filename << std::endl;
218
219
        // Parse the extracted data
220
1
        if (is_tap)
221
0
        {
222
0
            parseTap(data);
223
0
        }
224
1
        else
225
1
        {
226
1
            parseTzx(data);
227
1
        }
228
229
1
        return true;
230
1
    }
231
232
    // Handle non-zipped files
233
0
    if (endsWith(lowerFileName, ".tap"))
234
0
    {
235
        // Read file data
236
0
        std::ifstream file(fileName, std::ios::binary | std::ios::ate);
237
0
        if (!file.is_open())
238
0
        {
239
0
            std::cerr << "Failed to open file: " << fileName << std::endl;
240
0
            return false;
241
0
        }
242
243
0
        std::streamsize size = file.tellg();
244
0
        file.seekg(0, std::ios::beg);
245
246
0
        tapeData.resize(size);
247
0
        if (!file.read(reinterpret_cast<char *>(tapeData.data()), size))
248
0
        {
249
0
            std::cerr << "Failed to read file: " << fileName << std::endl;
250
0
            return false;
251
0
        }
252
253
0
        parseTap(tapeData);
254
0
        return true;
255
0
    }
256
0
    else if (endsWith(lowerFileName, ".tzx"))
257
0
    {
258
        // Read file data
259
0
        std::ifstream file(fileName, std::ios::binary | std::ios::ate);
260
0
        if (!file.is_open())
261
0
        {
262
0
            std::cerr << "Failed to open file: " << fileName << std::endl;
263
0
            return false;
264
0
        }
265
266
0
        std::streamsize size = file.tellg();
267
0
        file.seekg(0, std::ios::beg);
268
269
0
        tapeData.resize(size);
270
0
        if (!file.read(reinterpret_cast<char *>(tapeData.data()), size))
271
0
        {
272
0
            std::cerr << "Failed to read file: " << fileName << std::endl;
273
0
            return false;
274
0
        }
275
276
0
        parseTzx(tapeData);
277
0
        return true;
278
0
    }
279
280
0
    std::cerr << "Unsupported file format: " << fileName << std::endl;
281
0
    return false;
282
0
}
283
284
// Load virtual tape data directly
285
void Tape::loadVirtualTape(const std::vector<uint8_t> &data)
286
0
{
287
0
    std::cout << "Loading virtual tape with " << data.size() << " bytes" << std::endl;
288
0
    tapeData = data;
289
0
    parseTap(tapeData);
290
0
}
291
292
// Parse TAP file format
293
void Tape::parseTap(const std::vector<uint8_t> &data)
294
0
{
295
    // std::cout << "Parsing TAP file with " << data.size() << " bytes" << std::endl;
296
297
    // Clear any existing blocks
298
0
    tapBlocks.clear();
299
300
0
    size_t pos = 0;
301
0
    while (pos + 2 <= data.size())
302
0
    {
303
        // Read block length (2 bytes, little-endian)
304
0
        uint16_t blockLength = static_cast<uint16_t>(data[pos]) |
305
0
                               (static_cast<uint16_t>(data[pos + 1]) << 8);
306
307
        // Check if we have enough data for the complete block
308
0
        if (pos + 2 + blockLength > data.size())
309
0
        {
310
0
            std::cerr << "Incomplete block at position " << pos << std::endl;
311
0
            break;
312
0
        }
313
314
        // Create a new block
315
0
        TapBlock block;
316
0
        block.length = blockLength;
317
318
        // Extract all data (including flag and checksum)
319
0
        if (blockLength > 0)
320
0
        {
321
0
            block.data.assign(data.begin() + pos + 2, data.begin() + pos + 2 + blockLength);
322
0
        }
323
324
        // Extract flag byte from data for compatibility
325
0
        if (block.data.size() > 0)
326
0
        {
327
0
            block.flag = block.data[0];
328
0
        }
329
330
        // Extract checksum from data for compatibility
331
0
        if (block.data.size() > 0)
332
0
        {
333
0
            block.checksum = block.data.back();
334
0
        }
335
336
        // Validate checksum
337
0
        block.isValid = validateChecksum(block.data);
338
339
        // Parse header information if this is a header block
340
0
        if (block.flag == 0x00 && block.data.size() >= 18)
341
0
        { // Need at least 18 bytes for header (flag + 17 header bytes)
342
            // For header blocks, we need to parse the header info from data[1] to data[17]
343
            // We temporarily modify the block to match the expected format for parseHeaderInfo
344
0
            std::vector<uint8_t> tempData(block.data.begin() + 1, block.data.end() - 1); // Exclude flag and checksum
345
0
            TapBlock tempBlock = block;
346
0
            tempBlock.data = tempData;
347
0
            parseHeaderInfo(tempBlock);
348
            // Copy back the parsed header info
349
0
            block.fileType = tempBlock.fileType;
350
0
            block.filename = tempBlock.filename;
351
0
            block.dataLength = tempBlock.dataLength;
352
0
            block.param1 = tempBlock.param1;
353
0
            block.param2 = tempBlock.param2;
354
0
        }
355
356
        // Add block to our collection
357
0
        tapBlocks.push_back(block);
358
359
        // Move to next block
360
0
        pos += 2 + blockLength;
361
362
        // std::cout << "  Parsed block: length=" << blockLength
363
        //           << ", flag=0x" << std::hex << static_cast<int>(block.flag) << std::dec
364
        //           << ", data_size=" << block.data.size()
365
        //           << ", checksum_valid=" << (block.isValid ? "yes" : "no") << std::endl;
366
0
    }
367
368
0
    std::cout << "Parsed " << tapBlocks.size() << " blocks from TAP file" << std::endl;
369
0
}
370
371
// Parse TZX file format
372
// TZX is a more advanced tape format that supports various block types and timing parameters
373
// This function parses the TZX file header and then processes each block according to its type
374
void Tape::parseTzx(const std::vector<uint8_t> &data)
375
1
{
376
1
    std::cout << "Parsing TZX file with " << data.size() << " bytes" << std::endl;
377
378
    // Clear any existing blocks from previous loads
379
1
    tapBlocks.clear();
380
381
    // Check if file is large enough to contain a header (minimum 10 bytes)
382
1
    if (data.size() < 10)
383
0
    {
384
0
        std::cerr << "TZX file too small" << std::endl;
385
0
        return;
386
0
    }
387
388
    // Check signature "ZXTape!" followed by EOF marker (0x1A)
389
    // This identifies the file as a valid TZX format
390
1
    if (memcmp(data.data(), "ZXTape!", 7) != 0 || data[7] != 0x1A)
391
0
    {
392
0
        std::cerr << "Invalid TZX signature" << std::endl;
393
0
        return;
394
0
    }
395
396
    // Check version (we support 1.x versions)
397
    // Version is stored as two bytes: major.minor
398
1
    uint8_t majorVersion = data[8];
399
1
    uint8_t minorVersion = data[9];
400
1
    if (majorVersion != 1)
401
0
    {
402
0
        std::cerr << "Unsupported TZX version: " << (int)majorVersion << "." << (int)minorVersion << std::endl;
403
0
        return;
404
0
    }
405
406
1
    std::cout << "TZX version " << (int)majorVersion << "." << (int)minorVersion << std::endl;
407
408
    // Parse blocks starting after the 10-byte header
409
1
    size_t pos = 10; // Start after header
410
10
    while (pos < data.size())
411
9
    {
412
        // Make sure we have at least one byte for the block ID
413
9
        if (pos + 1 > data.size())
414
0
        {
415
0
            std::cerr << "Unexpected end of file" << std::endl;
416
0
            break;
417
0
        }
418
419
        // Read the block ID which determines the block type
420
9
        uint8_t blockId = data[pos];
421
9
        pos++;
422
423
        // Process each block according to its type
424
9
        switch (blockId)
425
9
        {
426
8
        case 0x10: // Standard speed data block (same as TAP format)
427
8
            pos = parseTzxStandardSpeedBlock(data, pos);
428
8
            break;
429
430
0
        case 0x11: // Turbo speed data block (custom timing parameters)
431
0
            pos = parseTzxTurboSpeedBlock(data, pos);
432
0
            break;
433
434
0
        case 0x12: // Pure tone (repeated pulses of the same length)
435
0
            pos = parseTzxPureToneBlock(data, pos);
436
0
            break;
437
438
0
        case 0x13: // Sequence of pulses of various lengths
439
0
            pos = parseTzxPulseSequenceBlock(data, pos);
440
0
            break;
441
442
0
        case 0x14: // Pure data block (raw data bits with custom timing)
443
0
            pos = parseTzxPureDataBlock(data, pos);
444
0
            break;
445
446
0
        case 0x15: // Direct recording block (raw audio samples)
447
0
            pos = parseTzxDirectRecordingBlock(data, pos);
448
0
            break;
449
450
0
        case 0x20: // Pause (silence) or 'Stop the tape'
451
0
            pos = parseTzxPauseBlock(data, pos);
452
0
            break;
453
454
0
        case 0x21: // Group start (beginning of a logical group of blocks)
455
0
            pos = parseTzxGroupStartBlock(data, pos);
456
0
            break;
457
458
0
        case 0x22: // Group end (end of a logical group of blocks)
459
0
            pos = parseTzxGroupEndBlock(data, pos);
460
0
            break;
461
462
0
        case 0x23: // Jump to block (change playback position)
463
0
            pos = parseTzxJumpBlock(data, pos);
464
0
            break;
465
466
0
        case 0x24: // Loop start (beginning of a loop)
467
0
            pos = parseTzxLoopStartBlock(data, pos);
468
0
            break;
469
470
0
        case 0x25: // Loop end (end of a loop)
471
0
            pos = parseTzxLoopEndBlock(data, pos);
472
0
            break;
473
474
0
        case 0x26: // Call sequence (call a subroutine)
475
0
            pos = parseTzxCallSequenceBlock(data, pos);
476
0
            break;
477
478
0
        case 0x27: // Return from sequence (return from subroutine)
479
0
            pos = parseTzxReturnSequenceBlock(data, pos);
480
0
            break;
481
482
0
        case 0x28: // Select block (offer choices to the user)
483
0
            pos = parseTzxSelectBlock(data, pos);
484
0
            break;
485
486
0
        case 0x2A: // Stop the tape if in 48K mode
487
0
            pos = parseTzxStop48KBlock(data, pos);
488
0
            break;
489
490
0
        case 0x2B: // Set signal level (force signal high or low)
491
0
            pos = parseTzxSetLevelBlock(data, pos);
492
0
            break;
493
494
0
        case 0x30: // Text description (informational text)
495
0
            pos = parseTzxTextDescriptionBlock(data, pos);
496
0
            break;
497
498
0
        case 0x31: // Message block (display message to user)
499
0
            pos = parseTzxMessageBlock(data, pos);
500
0
            break;
501
502
1
        case 0x32: // Archive info (metadata about the tape)
503
1
            pos = parseTzxArchiveInfoBlock(data, pos);
504
1
            break;
505
506
0
        case 0x33: // Hardware type (what hardware this tape supports)
507
0
            pos = parseTzxHardwareTypeBlock(data, pos);
508
0
            break;
509
510
0
        case 0x35: // Custom info block (application-specific data)
511
0
            pos = parseTzxCustomInfoBlock(data, pos);
512
0
            break;
513
514
0
        case 0x5A: // Glue block (connects multiple TZX files)
515
0
            pos = parseTzxGlueBlock(data, pos);
516
0
            break;
517
518
0
        default:
519
            // Unknown block type - try to skip it by reading its length
520
0
            std::cerr << "Unknown TZX block ID: 0x" << std::hex << (int)blockId << std::dec << std::endl;
521
            // Try to skip this block by reading its length
522
0
            if (pos + 4 <= data.size())
523
0
            {
524
                // Read 4-byte length field
525
0
                uint32_t blockLength = static_cast<uint32_t>(data[pos]) |
526
0
                                       (static_cast<uint32_t>(data[pos + 1]) << 8) |
527
0
                                       (static_cast<uint32_t>(data[pos + 2]) << 16) |
528
0
                                       (static_cast<uint32_t>(data[pos + 3]) << 24);
529
                // Skip the block data
530
0
                pos += 4 + blockLength;
531
0
            }
532
0
            else
533
0
            {
534
0
                std::cerr << "Unable to skip unknown block, file may be corrupt" << std::endl;
535
0
                return;
536
0
            }
537
0
            break;
538
9
        }
539
540
        // Safety check to prevent infinite loops
541
9
        if (pos > data.size())
542
0
        {
543
0
            std::cerr << "Block parsing went beyond file end" << std::endl;
544
0
            break;
545
0
        }
546
9
    }
547
548
1
    std::cout << "Parsed " << tapBlocks.size() << " blocks from TZX file" << std::endl;
549
1
}
550
551
// Parse TZX Standard Speed Data Block (ID 10)
552
// This is the most common block type, equivalent to the TAP format blocks
553
// It contains data with standard ZX Spectrum timing parameters
554
size_t Tape::parseTzxStandardSpeedBlock(const std::vector<uint8_t> &data, size_t pos)
555
8
{
556
    // Check if we have enough data for the block header (4 bytes minimum)
557
8
    if (pos + 4 > data.size())
558
0
    {
559
0
        std::cerr << "Incomplete TZX Standard Speed Data Block" << std::endl;
560
0
        return data.size();
561
0
    }
562
563
    // Read pause duration (2 bytes, little-endian)
564
    // This is the delay after the block in milliseconds
565
8
    uint16_t pauseDuration = static_cast<uint16_t>(data[pos]) |
566
8
                             (static_cast<uint16_t>(data[pos + 1]) << 8);
567
8
    pos += 2;
568
569
    // Read data length (2 bytes, little-endian)
570
    // This tells us how many bytes of data follow
571
8
    uint16_t dataLength = static_cast<uint16_t>(data[pos]) |
572
8
                          (static_cast<uint16_t>(data[pos + 1]) << 8);
573
8
    pos += 2;
574
575
    // Check if we have enough data for the actual block data
576
8
    if (pos + dataLength > data.size())
577
0
    {
578
0
        std::cerr << "Incomplete TZX Standard Speed Data Block data" << std::endl;
579
0
        return data.size();
580
0
    }
581
582
    // Create a new TapBlock to store this data
583
8
    TapBlock block;
584
8
    block.length = dataLength;
585
586
    // Extract all data (including flag and checksum)
587
    // The data includes the flag byte (first), the actual data, and the checksum (last)
588
8
    if (dataLength > 0)
589
8
    {
590
8
        block.data.assign(data.begin() + pos, data.begin() + pos + dataLength);
591
8
    }
592
593
    // Extract flag byte from data for compatibility
594
    // The flag byte indicates if this is a header (0x00) or data (0xFF) block
595
8
    if (block.data.size() > 0)
596
8
    {
597
8
        block.flag = block.data[0];
598
8
    }
599
600
    // Extract checksum from data for compatibility
601
    // The checksum is the last byte of the data and is used for error detection
602
8
    if (block.data.size() > 0)
603
8
    {
604
8
        block.checksum = block.data.back();
605
8
    }
606
607
    // Validate checksum to check data integrity
608
8
    block.isValid = validateChecksum(block.data);
609
610
    // Parse header information if this is a header block
611
    // Header blocks contain metadata about the following data block
612
8
    if (block.flag == 0x00 && block.data.size() >= 18)
613
4
    { // Need at least 18 bytes for header (flag + 17 header bytes)
614
        // For header blocks, we need to parse the header info from data[1] to data[17]
615
        // We temporarily modify the block to match the expected format for parseHeaderInfo
616
4
        std::vector<uint8_t> tempData(block.data.begin() + 1, block.data.end() - 1); // Exclude flag and checksum
617
4
        TapBlock tempBlock = block;
618
4
        tempBlock.data = tempData;
619
4
        parseHeaderInfo(tempBlock);
620
        // Copy back the parsed header info
621
4
        block.fileType = tempBlock.fileType;
622
4
        block.filename = tempBlock.filename;
623
4
        block.dataLength = tempBlock.dataLength;
624
4
        block.param1 = tempBlock.param1;
625
4
        block.param2 = tempBlock.param2;
626
4
    }
627
628
    // Add block to our collection
629
8
    tapBlocks.push_back(block);
630
631
    // Move position past the data to the next block
632
8
    pos += dataLength;
633
634
8
    return pos;
635
8
}
636
637
// Parse TZX Turbo Speed Data Block (ID 11)
638
// This block type allows custom timing parameters for faster loading
639
// It's an enhanced version of the standard speed block with configurable pulse durations
640
size_t Tape::parseTzxTurboSpeedBlock(const std::vector<uint8_t> &data, size_t pos)
641
0
{
642
    // Check if we have enough data for the block header (18 bytes minimum)
643
0
    if (pos + 18 > data.size())
644
0
    {
645
0
        std::cerr << "Incomplete TZX Turbo Speed Data Block header" << std::endl;
646
0
        return data.size();
647
0
    }
648
649
    // Read all the timing parameters for this turbo block
650
    // These override the standard timings used in regular TAP files
651
652
    // Pilot pulse length (2 bytes, little-endian)
653
    // Duration of each pulse in the pilot tone
654
0
    uint16_t pilotPulseLength = static_cast<uint16_t>(data[pos]) |
655
0
                                (static_cast<uint16_t>(data[pos + 1]) << 8);
656
0
    pos += 2;
657
658
    // Sync pulse 1 length (2 bytes, little-endian)
659
    // Duration of the first synchronization pulse
660
0
    uint16_t sync1PulseLength = static_cast<uint16_t>(data[pos]) |
661
0
                                (static_cast<uint16_t>(data[pos + 1]) << 8);
662
0
    pos += 2;
663
664
    // Sync pulse 2 length (2 bytes, little-endian)
665
    // Duration of the second synchronization pulse
666
0
    uint16_t sync2PulseLength = static_cast<uint16_t>(data[pos]) |
667
0
                                (static_cast<uint16_t>(data[pos + 1]) << 8);
668
0
    pos += 2;
669
670
    // Zero bit pulse length (2 bytes, little-endian)
671
    // Duration of pulses representing a binary 0
672
0
    uint16_t zeroBitPulseLength = static_cast<uint16_t>(data[pos]) |
673
0
                                  (static_cast<uint16_t>(data[pos + 1]) << 8);
674
0
    pos += 2;
675
676
    // One bit pulse length (2 bytes, little-endian)
677
    // Duration of pulses representing a binary 1
678
0
    uint16_t oneBitPulseLength = static_cast<uint16_t>(data[pos]) |
679
0
                                 (static_cast<uint16_t>(data[pos + 1]) << 8);
680
0
    pos += 2;
681
682
    // Pilot tone length (2 bytes, little-endian)
683
    // Number of pilot pulses to generate
684
0
    uint16_t pilotToneLength = static_cast<uint16_t>(data[pos]) |
685
0
                               (static_cast<uint16_t>(data[pos + 1]) << 8);
686
0
    pos += 2;
687
688
    // Used bits in last byte (1 byte)
689
    // Number of bits used in the final byte (1-8)
690
0
    uint8_t usedBitsInLastByte = data[pos];
691
0
    pos += 1;
692
693
    // Pause duration after block (2 bytes, little-endian)
694
    // Duration of silence after this block in milliseconds
695
0
    uint16_t pauseDuration = static_cast<uint16_t>(data[pos]) |
696
0
                             (static_cast<uint16_t>(data[pos + 1]) << 8);
697
0
    pos += 2;
698
699
    // Read data length (3 bytes, little-endian)
700
    // How many bytes of actual data follow
701
0
    uint32_t dataLength = static_cast<uint32_t>(data[pos]) |
702
0
                          (static_cast<uint32_t>(data[pos + 1]) << 8) |
703
0
                          (static_cast<uint32_t>(data[pos + 2]) << 16);
704
0
    pos += 3;
705
706
    // Check if we have enough data for the actual block data
707
0
    if (pos + dataLength > data.size())
708
0
    {
709
0
        std::cerr << "Incomplete TZX Turbo Speed Data Block data" << std::endl;
710
0
        return data.size();
711
0
    }
712
713
    // Create a new TapBlock to store this data
714
0
    TapBlock block;
715
0
    block.length = static_cast<uint16_t>(dataLength & 0xFFFF); // Truncate to 16-bit for compatibility
716
717
    // Extract all data (including flag and checksum)
718
    // The data includes the flag byte (first), the actual data, and the checksum (last)
719
0
    if (dataLength > 0)
720
0
    {
721
0
        block.data.assign(data.begin() + pos, data.begin() + pos + dataLength);
722
0
    }
723
724
    // Extract flag byte from data for compatibility
725
    // The flag byte indicates if this is a header (0x00) or data (0xFF) block
726
0
    if (block.data.size() > 0)
727
0
    {
728
0
        block.flag = block.data[0];
729
0
    }
730
731
    // Extract checksum from data for compatibility
732
    // The checksum is the last byte of the data and is used for error detection
733
0
    if (block.data.size() > 0)
734
0
    {
735
0
        block.checksum = block.data.back();
736
0
    }
737
738
    // Validate checksum to check data integrity
739
0
    block.isValid = validateChecksum(block.data);
740
741
    // Parse header information if this is a header block
742
    // Header blocks contain metadata about the following data block
743
0
    if (block.flag == 0x00 && block.data.size() >= 18)
744
0
    { // Need at least 18 bytes for header (flag + 17 header bytes)
745
        // For header blocks, we need to parse the header info from data[1] to data[17]
746
        // We temporarily modify the block to match the expected format for parseHeaderInfo
747
0
        std::vector<uint8_t> tempData(block.data.begin() + 1, block.data.end() - 1); // Exclude flag and checksum
748
0
        TapBlock tempBlock = block;
749
0
        tempBlock.data = tempData;
750
0
        parseHeaderInfo(tempBlock);
751
        // Copy back the parsed header info
752
0
        block.fileType = tempBlock.fileType;
753
0
        block.filename = tempBlock.filename;
754
0
        block.dataLength = tempBlock.dataLength;
755
0
        block.param1 = tempBlock.param1;
756
0
        block.param2 = tempBlock.param2;
757
0
    }
758
759
    // Add block to our collection
760
0
    tapBlocks.push_back(block);
761
762
    // Move position past the data to the next block
763
0
    pos += dataLength;
764
765
0
    return pos;
766
0
}
767
768
// Parse TZX Pure Tone Block (ID 12)
769
// This block generates a series of pulses with the same duration
770
// Often used for the initial pilot tone that helps the Spectrum detect the start of data
771
size_t Tape::parseTzxPureToneBlock(const std::vector<uint8_t> &data, size_t pos)
772
0
{
773
    // Check if we have enough data for the block header (4 bytes minimum)
774
0
    if (pos + 4 > data.size())
775
0
    {
776
0
        std::cerr << "Incomplete TZX Pure Tone Block" << std::endl;
777
0
        return data.size();
778
0
    }
779
780
    // Read pulse length (2 bytes, little-endian)
781
    // This is the duration of each individual pulse in T-states (CPU cycles)
782
0
    uint16_t pulseLength = static_cast<uint16_t>(data[pos]) |
783
0
                           (static_cast<uint16_t>(data[pos + 1]) << 8);
784
0
    pos += 2;
785
786
    // Read number of pulses (2 bytes, little-endian)
787
    // How many identical pulses to generate
788
0
    uint16_t pulseCount = static_cast<uint16_t>(data[pos]) |
789
0
                          (static_cast<uint16_t>(data[pos + 1]) << 8);
790
0
    pos += 2;
791
792
    // This block doesn't contain data for TapBlocks, so we just skip it
793
    // Pure tone blocks are used for synchronization, not data storage
794
0
    std::cout << "Skipping TZX Pure Tone Block: " << pulseCount << " pulses of " << pulseLength << " T-states" << std::endl;
795
796
0
    return pos;
797
0
}
798
799
// Parse TZX Pulse Sequence Block (ID 13)
800
// This block defines a sequence of pulses with different durations
801
// Useful for non-standard tape encoding schemes
802
size_t Tape::parseTzxPulseSequenceBlock(const std::vector<uint8_t> &data, size_t pos)
803
0
{
804
    // Check if we have enough data for the pulse count byte
805
0
    if (pos + 1 > data.size())
806
0
    {
807
0
        std::cerr << "Incomplete TZX Pulse Sequence Block" << std::endl;
808
0
        return data.size();
809
0
    }
810
811
    // Read number of pulses in this sequence
812
0
    uint8_t pulseCount = data[pos];
813
0
    pos += 1;
814
815
    // Check if we have enough data for all pulse durations
816
    // Each pulse duration is 2 bytes, so we need 2*pulseCount bytes
817
0
    if (pos + (2 * pulseCount) > data.size())
818
0
    {
819
0
        std::cerr << "Incomplete TZX Pulse Sequence Block data" << std::endl;
820
0
        return data.size();
821
0
    }
822
823
    // Skip all pulse lengths (2 bytes each)
824
    // In a real implementation, we would process these pulse durations
825
0
    pos += 2 * pulseCount;
826
827
    // This block doesn't contain data for TapBlocks, so we just skip it
828
    // Pulse sequence blocks are used for custom waveforms, not standard data
829
0
    std::cout << "Skipping TZX Pulse Sequence Block: " << (int)pulseCount << " pulses" << std::endl;
830
831
0
    return pos;
832
0
}
833
834
// Parse TZX Pure Data Block (ID 14)
835
// This block contains raw data bits with custom timing parameters
836
// Unlike standard blocks, it doesn't include pilot or sync pulses
837
size_t Tape::parseTzxPureDataBlock(const std::vector<uint8_t> &data, size_t pos)
838
0
{
839
    // Check if we have enough data for the block header (10 bytes minimum)
840
0
    if (pos + 10 > data.size())
841
0
    {
842
0
        std::cerr << "Incomplete TZX Pure Data Block header" << std::endl;
843
0
        return data.size();
844
0
    }
845
846
    // Read zero bit pulse length (2 bytes, little-endian)
847
    // Duration of pulses representing a binary 0
848
0
    uint16_t zeroBitPulseLength = static_cast<uint16_t>(data[pos]) |
849
0
                                  (static_cast<uint16_t>(data[pos + 1]) << 8);
850
0
    pos += 2;
851
852
    // Read one bit pulse length (2 bytes, little-endian)
853
    // Duration of pulses representing a binary 1
854
0
    uint16_t oneBitPulseLength = static_cast<uint16_t>(data[pos]) |
855
0
                                 (static_cast<uint16_t>(data[pos + 1]) << 8);
856
0
    pos += 2;
857
858
    // Read used bits in last byte (1 byte)
859
    // Number of bits used in the final byte (1-8)
860
0
    uint8_t usedBitsInLastByte = data[pos];
861
0
    pos += 1;
862
863
    // Read pause duration (2 bytes, little-endian)
864
    // Duration of silence after this block in milliseconds
865
0
    uint16_t pauseDuration = static_cast<uint16_t>(data[pos]) |
866
0
                             (static_cast<uint16_t>(data[pos + 1]) << 8);
867
0
    pos += 2;
868
869
    // Read data length (3 bytes, little-endian)
870
    // How many bytes of actual data follow
871
0
    uint32_t dataLength = static_cast<uint32_t>(data[pos]) |
872
0
                          (static_cast<uint32_t>(data[pos + 1]) << 8) |
873
0
                          (static_cast<uint32_t>(data[pos + 2]) << 16);
874
0
    pos += 3;
875
876
    // Check if we have enough data for the actual block data
877
0
    if (pos + dataLength > data.size())
878
0
    {
879
0
        std::cerr << "Incomplete TZX Pure Data Block data" << std::endl;
880
0
        return data.size();
881
0
    }
882
883
    // Create a new TapBlock to store this data
884
0
    TapBlock block;
885
0
    block.length = static_cast<uint16_t>(dataLength & 0xFFFF); // Truncate to 16-bit for compatibility
886
887
    // Extract all data (including flag and checksum)
888
    // The data includes the flag byte (first), the actual data, and the checksum (last)
889
0
    if (dataLength > 0)
890
0
    {
891
0
        block.data.assign(data.begin() + pos, data.begin() + pos + dataLength);
892
0
    }
893
894
    // Extract flag byte from data for compatibility
895
    // The flag byte indicates if this is a header (0x00) or data (0xFF) block
896
0
    if (block.data.size() > 0)
897
0
    {
898
0
        block.flag = block.data[0];
899
0
    }
900
901
    // Extract checksum from data for compatibility
902
    // The checksum is the last byte of the data and is used for error detection
903
0
    if (block.data.size() > 0)
904
0
    {
905
0
        block.checksum = block.data.back();
906
0
    }
907
908
    // Validate checksum to check data integrity
909
0
    block.isValid = validateChecksum(block.data);
910
911
    // Parse header information if this is a header block
912
    // Header blocks contain metadata about the following data block
913
0
    if (block.flag == 0x00 && block.data.size() >= 18)
914
0
    { // Need at least 18 bytes for header (flag + 17 header bytes)
915
        // For header blocks, we need to parse the header info from data[1] to data[17]
916
        // We temporarily modify the block to match the expected format for parseHeaderInfo
917
0
        std::vector<uint8_t> tempData(block.data.begin() + 1, block.data.end() - 1); // Exclude flag and checksum
918
0
        TapBlock tempBlock = block;
919
0
        tempBlock.data = tempData;
920
0
        parseHeaderInfo(tempBlock);
921
        // Copy back the parsed header info
922
0
        block.fileType = tempBlock.fileType;
923
0
        block.filename = tempBlock.filename;
924
0
        block.dataLength = tempBlock.dataLength;
925
0
        block.param1 = tempBlock.param1;
926
0
        block.param2 = tempBlock.param2;
927
0
    }
928
929
    // Add block to our collection
930
0
    tapBlocks.push_back(block);
931
932
    // Move position past the data to the next block
933
0
    pos += dataLength;
934
935
0
    return pos;
936
0
}
937
938
// Parse TZX Direct Recording Block (ID 15)
939
size_t Tape::parseTzxDirectRecordingBlock(const std::vector<uint8_t> &data, size_t pos)
940
0
{
941
0
    if (pos + 8 > data.size())
942
0
    {
943
0
        std::cerr << "Incomplete TZX Direct Recording Block header" << std::endl;
944
0
        return data.size();
945
0
    }
946
947
    // Read T-states per sample (2 bytes)
948
0
    uint16_t tStatesPerSample = static_cast<uint16_t>(data[pos]) |
949
0
                                (static_cast<uint16_t>(data[pos + 1]) << 8);
950
0
    pos += 2;
951
952
    // Read pause duration (2 bytes)
953
0
    uint16_t pauseDuration = static_cast<uint16_t>(data[pos]) |
954
0
                             (static_cast<uint16_t>(data[pos + 1]) << 8);
955
0
    pos += 2;
956
957
    // Read used bits in last byte (1 byte)
958
0
    uint8_t usedBitsInLastByte = data[pos];
959
0
    pos += 1;
960
961
    // Read data length (3 bytes)
962
0
    uint32_t dataLength = static_cast<uint32_t>(data[pos]) |
963
0
                          (static_cast<uint32_t>(data[pos + 1]) << 8) |
964
0
                          (static_cast<uint32_t>(data[pos + 2]) << 16);
965
0
    pos += 3;
966
967
    // Check if we have enough data
968
0
    if (pos + dataLength > data.size())
969
0
    {
970
0
        std::cerr << "Incomplete TZX Direct Recording Block data" << std::endl;
971
0
        return data.size();
972
0
    }
973
974
    // This block contains raw samples, not structured data like TAP blocks
975
    // We could convert it to a TapBlock, but it's complex and may not be necessary
976
0
    std::cout << "Skipping TZX Direct Recording Block: " << dataLength << " samples" << std::endl;
977
978
    // Move position past the data
979
0
    pos += dataLength;
980
981
0
    return pos;
982
0
}
983
984
// Parse TZX Pause Block (ID 20)
985
size_t Tape::parseTzxPauseBlock(const std::vector<uint8_t> &data, size_t pos)
986
0
{
987
0
    if (pos + 2 > data.size())
988
0
    {
989
0
        std::cerr << "Incomplete TZX Pause Block" << std::endl;
990
0
        return data.size();
991
0
    }
992
993
    // Read pause duration (2 bytes)
994
0
    uint16_t pauseDuration = static_cast<uint16_t>(data[pos]) |
995
0
                             (static_cast<uint16_t>(data[pos + 1]) << 8);
996
0
    pos += 2;
997
998
    // This block doesn't contain data for TapBlocks, so we just skip it
999
0
    std::cout << "Skipping TZX Pause Block: " << pauseDuration << " ms" << std::endl;
1000
1001
0
    return pos;
1002
0
}
1003
1004
// Parse TZX Group Start Block (ID 21)
1005
size_t Tape::parseTzxGroupStartBlock(const std::vector<uint8_t> &data, size_t pos)
1006
0
{
1007
0
    if (pos + 1 > data.size())
1008
0
    {
1009
0
        std::cerr << "Incomplete TZX Group Start Block" << std::endl;
1010
0
        return data.size();
1011
0
    }
1012
1013
    // Read group name length
1014
0
    uint8_t groupNameLength = data[pos];
1015
0
    pos += 1;
1016
1017
    // Check if we have enough data for the group name
1018
0
    if (pos + groupNameLength > data.size())
1019
0
    {
1020
0
        std::cerr << "Incomplete TZX Group Start Block data" << std::endl;
1021
0
        return data.size();
1022
0
    }
1023
1024
    // Read group name
1025
0
    std::string groupName(data.begin() + pos, data.begin() + pos + groupNameLength);
1026
0
    pos += groupNameLength;
1027
1028
    // This block doesn't contain data for TapBlocks, so we just skip it
1029
0
    std::cout << "Skipping TZX Group Start Block: " << groupName << std::endl;
1030
1031
0
    return pos;
1032
0
}
1033
1034
// Parse TZX Group End Block (ID 22)
1035
size_t Tape::parseTzxGroupEndBlock(const std::vector<uint8_t> &data, size_t pos)
1036
0
{
1037
    // This block has no body, so we just return the current position
1038
0
    std::cout << "Skipping TZX Group End Block" << std::endl;
1039
0
    return pos;
1040
0
}
1041
1042
// Parse TZX Jump Block (ID 23)
1043
size_t Tape::parseTzxJumpBlock(const std::vector<uint8_t> &data, size_t pos)
1044
0
{
1045
0
    if (pos + 2 > data.size())
1046
0
    {
1047
0
        std::cerr << "Incomplete TZX Jump Block" << std::endl;
1048
0
        return data.size();
1049
0
    }
1050
1051
    // Read relative jump value (2 bytes, signed)
1052
0
    int16_t jumpValue = static_cast<int16_t>(data[pos]) |
1053
0
                        (static_cast<int16_t>(data[pos + 1]) << 8);
1054
0
    pos += 2;
1055
1056
    // This block affects control flow, but we're just parsing sequentially
1057
0
    std::cout << "Skipping TZX Jump Block: " << jumpValue << std::endl;
1058
1059
0
    return pos;
1060
0
}
1061
1062
// Parse TZX Loop Start Block (ID 24)
1063
size_t Tape::parseTzxLoopStartBlock(const std::vector<uint8_t> &data, size_t pos)
1064
0
{
1065
0
    if (pos + 2 > data.size())
1066
0
    {
1067
0
        std::cerr << "Incomplete TZX Loop Start Block" << std::endl;
1068
0
        return data.size();
1069
0
    }
1070
1071
    // Read number of repetitions (2 bytes)
1072
0
    uint16_t repetitions = static_cast<uint16_t>(data[pos]) |
1073
0
                           (static_cast<uint16_t>(data[pos + 1]) << 8);
1074
0
    pos += 2;
1075
1076
    // This block affects control flow, but we're just parsing sequentially
1077
0
    std::cout << "Skipping TZX Loop Start Block: " << repetitions << " repetitions" << std::endl;
1078
1079
0
    return pos;
1080
0
}
1081
1082
// Parse TZX Loop End Block (ID 25)
1083
size_t Tape::parseTzxLoopEndBlock(const std::vector<uint8_t> &data, size_t pos)
1084
0
{
1085
    // This block has no body, so we just return the current position
1086
0
    std::cout << "Skipping TZX Loop End Block" << std::endl;
1087
0
    return pos;
1088
0
}
1089
1090
// Parse TZX Call Sequence Block (ID 26)
1091
size_t Tape::parseTzxCallSequenceBlock(const std::vector<uint8_t> &data, size_t pos)
1092
0
{
1093
0
    if (pos + 2 > data.size())
1094
0
    {
1095
0
        std::cerr << "Incomplete TZX Call Sequence Block" << std::endl;
1096
0
        return data.size();
1097
0
    }
1098
1099
    // Read number of calls (2 bytes)
1100
0
    uint16_t callCount = static_cast<uint16_t>(data[pos]) |
1101
0
                         (static_cast<uint16_t>(data[pos + 1]) << 8);
1102
0
    pos += 2;
1103
1104
    // Check if we have enough data for all calls
1105
0
    if (pos + (2 * callCount) > data.size())
1106
0
    {
1107
0
        std::cerr << "Incomplete TZX Call Sequence Block data" << std::endl;
1108
0
        return data.size();
1109
0
    }
1110
1111
    // Skip all call offsets (2 bytes each)
1112
0
    pos += 2 * callCount;
1113
1114
    // This block affects control flow, but we're just parsing sequentially
1115
0
    std::cout << "Skipping TZX Call Sequence Block: " << callCount << " calls" << std::endl;
1116
1117
0
    return pos;
1118
0
}
1119
1120
// Parse TZX Return Sequence Block (ID 27)
1121
size_t Tape::parseTzxReturnSequenceBlock(const std::vector<uint8_t> &data, size_t pos)
1122
0
{
1123
    // This block has no body, so we just return the current position
1124
0
    std::cout << "Skipping TZX Return Sequence Block" << std::endl;
1125
0
    return pos;
1126
0
}
1127
1128
// Parse TZX Select Block (ID 28)
1129
size_t Tape::parseTzxSelectBlock(const std::vector<uint8_t> &data, size_t pos)
1130
0
{
1131
0
    if (pos + 2 > data.size())
1132
0
    {
1133
0
        std::cerr << "Incomplete TZX Select Block" << std::endl;
1134
0
        return data.size();
1135
0
    }
1136
1137
    // Read block length (2 bytes)
1138
0
    uint16_t blockLength = static_cast<uint16_t>(data[pos]) |
1139
0
                           (static_cast<uint16_t>(data[pos + 1]) << 8);
1140
0
    pos += 2;
1141
1142
    // Check if we have enough data for the rest of the block
1143
0
    if (pos + blockLength > data.size())
1144
0
    {
1145
0
        std::cerr << "Incomplete TZX Select Block data" << std::endl;
1146
0
        return data.size();
1147
0
    }
1148
1149
    // Move position past the block data
1150
0
    pos += blockLength;
1151
1152
    // This block doesn't contain data for TapBlocks, so we just skip it
1153
0
    std::cout << "Skipping TZX Select Block" << std::endl;
1154
1155
0
    return pos;
1156
0
}
1157
1158
// Parse TZX Stop the Tape if in 48K Mode Block (ID 2A)
1159
size_t Tape::parseTzxStop48KBlock(const std::vector<uint8_t> &data, size_t pos)
1160
0
{
1161
0
    if (pos + 4 > data.size())
1162
0
    {
1163
0
        std::cerr << "Incomplete TZX Stop the Tape if in 48K Mode Block" << std::endl;
1164
0
        return data.size();
1165
0
    }
1166
1167
    // Read block length (4 bytes) - should be 0
1168
0
    uint32_t blockLength = static_cast<uint32_t>(data[pos]) |
1169
0
                           (static_cast<uint32_t>(data[pos + 1]) << 8) |
1170
0
                           (static_cast<uint32_t>(data[pos + 2]) << 16) |
1171
0
                           (static_cast<uint32_t>(data[pos + 3]) << 24);
1172
0
    pos += 4;
1173
1174
    // This block has no additional data, so we just return the current position
1175
0
    std::cout << "Skipping TZX Stop the Tape if in 48K Mode Block" << std::endl;
1176
1177
0
    return pos;
1178
0
}
1179
1180
// Parse TZX Set Signal Level Block (ID 2B)
1181
size_t Tape::parseTzxSetLevelBlock(const std::vector<uint8_t> &data, size_t pos)
1182
0
{
1183
0
    if (pos + 5 > data.size())
1184
0
    {
1185
0
        std::cerr << "Incomplete TZX Set Signal Level Block" << std::endl;
1186
0
        return data.size();
1187
0
    }
1188
1189
    // Read block length (4 bytes) - should be 1
1190
0
    uint32_t blockLength = static_cast<uint32_t>(data[pos]) |
1191
0
                           (static_cast<uint32_t>(data[pos + 1]) << 8) |
1192
0
                           (static_cast<uint32_t>(data[pos + 2]) << 16) |
1193
0
                           (static_cast<uint32_t>(data[pos + 3]) << 24);
1194
0
    pos += 4;
1195
1196
    // Read signal level (1 byte)
1197
0
    uint8_t signalLevel = data[pos];
1198
0
    pos += 1;
1199
1200
    // This block doesn't contain data for TapBlocks, so we just skip it
1201
0
    std::cout << "Skipping TZX Set Signal Level Block: " << (int)signalLevel << std::endl;
1202
1203
0
    return pos;
1204
0
}
1205
1206
// Parse TZX Text Description Block (ID 30)
1207
size_t Tape::parseTzxTextDescriptionBlock(const std::vector<uint8_t> &data, size_t pos)
1208
0
{
1209
0
    if (pos + 1 > data.size())
1210
0
    {
1211
0
        std::cerr << "Incomplete TZX Text Description Block" << std::endl;
1212
0
        return data.size();
1213
0
    }
1214
1215
    // Read text length
1216
0
    uint8_t textLength = data[pos];
1217
0
    pos += 1;
1218
1219
    // Check if we have enough data for the text
1220
0
    if (pos + textLength > data.size())
1221
0
    {
1222
0
        std::cerr << "Incomplete TZX Text Description Block data" << std::endl;
1223
0
        return data.size();
1224
0
    }
1225
1226
    // Read text
1227
0
    std::string text(data.begin() + pos, data.begin() + pos + textLength);
1228
0
    pos += textLength;
1229
1230
    // This block doesn't contain data for TapBlocks, so we just skip it
1231
0
    std::cout << "Skipping TZX Text Description Block: " << text << std::endl;
1232
1233
0
    return pos;
1234
0
}
1235
1236
// Parse TZX Message Block (ID 31)
1237
size_t Tape::parseTzxMessageBlock(const std::vector<uint8_t> &data, size_t pos)
1238
0
{
1239
0
    if (pos + 2 > data.size())
1240
0
    {
1241
0
        std::cerr << "Incomplete TZX Message Block" << std::endl;
1242
0
        return data.size();
1243
0
    }
1244
1245
    // Read time to display (1 byte)
1246
0
    uint8_t timeToDisplay = data[pos];
1247
0
    pos += 1;
1248
1249
    // Read message length (1 byte)
1250
0
    uint8_t messageLength = data[pos];
1251
0
    pos += 1;
1252
1253
    // Check if we have enough data for the message
1254
0
    if (pos + messageLength > data.size())
1255
0
    {
1256
0
        std::cerr << "Incomplete TZX Message Block data" << std::endl;
1257
0
        return data.size();
1258
0
    }
1259
1260
    // Read message
1261
0
    std::string message(data.begin() + pos, data.begin() + pos + messageLength);
1262
0
    pos += messageLength;
1263
1264
    // This block doesn't contain data for TapBlocks, so we just skip it
1265
0
    std::cout << "Skipping TZX Message Block: " << message << std::endl;
1266
1267
0
    return pos;
1268
0
}
1269
1270
// Parse TZX Archive Info Block (ID 32)
1271
size_t Tape::parseTzxArchiveInfoBlock(const std::vector<uint8_t> &data, size_t pos)
1272
1
{
1273
1
    if (pos + 2 > data.size())
1274
0
    {
1275
0
        std::cerr << "Incomplete TZX Archive Info Block" << std::endl;
1276
0
        return data.size();
1277
0
    }
1278
1279
    // Read block length (2 bytes)
1280
1
    uint16_t blockLength = static_cast<uint16_t>(data[pos]) |
1281
1
                           (static_cast<uint16_t>(data[pos + 1]) << 8);
1282
1
    pos += 2;
1283
1284
    // Check if we have enough data for the rest of the block
1285
1
    if (pos + blockLength > data.size())
1286
0
    {
1287
0
        std::cerr << "Incomplete TZX Archive Info Block data" << std::endl;
1288
0
        return data.size();
1289
0
    }
1290
1291
    // Read number of strings
1292
1
    if (pos + 1 > data.size())
1293
0
    {
1294
0
        std::cerr << "Incomplete TZX Archive Info Block string count" << std::endl;
1295
0
        return data.size();
1296
0
    }
1297
1298
1
    uint8_t stringCount = data[pos];
1299
1
    pos += 1;
1300
1301
    // Skip all strings
1302
11
    for (uint8_t i = 0; i < stringCount; i++)
1303
10
    {
1304
10
        if (pos + 2 > data.size())
1305
0
        {
1306
0
            std::cerr << "Incomplete TZX Archive Info Block string" << std::endl;
1307
0
            return data.size();
1308
0
        }
1309
1310
        // Read text ID (1 byte)
1311
10
        uint8_t textId = data[pos];
1312
10
        pos += 1;
1313
1314
        // Read text length (1 byte)
1315
10
        uint8_t textLength = data[pos];
1316
10
        pos += 1;
1317
1318
        // Check if we have enough data for the text
1319
10
        if (pos + textLength > data.size())
1320
0
        {
1321
0
            std::cerr << "Incomplete TZX Archive Info Block text" << std::endl;
1322
0
            return data.size();
1323
0
        }
1324
1325
        // Skip the text
1326
10
        pos += textLength;
1327
10
    }
1328
1329
    // This block doesn't contain data for TapBlocks, so we just skip it
1330
1
    std::cout << "Skipping TZX Archive Info Block: " << (int)stringCount << " strings" << std::endl;
1331
1332
1
    return pos;
1333
1
}
1334
1335
// Parse TZX Hardware Type Block (ID 33)
1336
size_t Tape::parseTzxHardwareTypeBlock(const std::vector<uint8_t> &data, size_t pos)
1337
0
{
1338
0
    if (pos + 1 > data.size())
1339
0
    {
1340
0
        std::cerr << "Incomplete TZX Hardware Type Block" << std::endl;
1341
0
        return data.size();
1342
0
    }
1343
1344
    // Read number of hardware entries
1345
0
    uint8_t hardwareCount = data[pos];
1346
0
    pos += 1;
1347
1348
    // Check if we have enough data for all hardware entries
1349
0
    if (pos + (3 * hardwareCount) > data.size())
1350
0
    {
1351
0
        std::cerr << "Incomplete TZX Hardware Type Block data" << std::endl;
1352
0
        return data.size();
1353
0
    }
1354
1355
    // Skip all hardware entries (3 bytes each)
1356
0
    pos += 3 * hardwareCount;
1357
1358
    // This block doesn't contain data for TapBlocks, so we just skip it
1359
0
    std::cout << "Skipping TZX Hardware Type Block: " << (int)hardwareCount << " entries" << std::endl;
1360
1361
0
    return pos;
1362
0
}
1363
1364
// Parse TZX Custom Info Block (ID 35)
1365
size_t Tape::parseTzxCustomInfoBlock(const std::vector<uint8_t> &data, size_t pos)
1366
0
{
1367
0
    if (pos + 14 > data.size())
1368
0
    {
1369
0
        std::cerr << "Incomplete TZX Custom Info Block header" << std::endl;
1370
0
        return data.size();
1371
0
    }
1372
1373
    // Read identification string (10 bytes)
1374
0
    std::string idString(data.begin() + pos, data.begin() + pos + 10);
1375
0
    pos += 10;
1376
1377
    // Read custom info length (4 bytes)
1378
0
    uint32_t infoLength = static_cast<uint32_t>(data[pos]) |
1379
0
                          (static_cast<uint32_t>(data[pos + 1]) << 8) |
1380
0
                          (static_cast<uint32_t>(data[pos + 2]) << 16) |
1381
0
                          (static_cast<uint32_t>(data[pos + 3]) << 24);
1382
0
    pos += 4;
1383
1384
    // Check if we have enough data for the custom info
1385
0
    if (pos + infoLength > data.size())
1386
0
    {
1387
0
        std::cerr << "Incomplete TZX Custom Info Block data" << std::endl;
1388
0
        return data.size();
1389
0
    }
1390
1391
    // Move position past the custom info
1392
0
    pos += infoLength;
1393
1394
    // This block doesn't contain data for TapBlocks, so we just skip it
1395
0
    std::cout << "Skipping TZX Custom Info Block: " << idString << std::endl;
1396
1397
0
    return pos;
1398
0
}
1399
1400
// Parse TZX Glue Block (ID 5A)
1401
size_t Tape::parseTzxGlueBlock(const std::vector<uint8_t> &data, size_t pos)
1402
0
{
1403
0
    if (pos + 9 > data.size())
1404
0
    {
1405
0
        std::cerr << "Incomplete TZX Glue Block" << std::endl;
1406
0
        return data.size();
1407
0
    }
1408
1409
    // Check the glue block signature
1410
0
    if (memcmp(data.data() + pos, "XTape!", 6) != 0 || data[pos + 6] != 0x1A)
1411
0
    {
1412
0
        std::cerr << "Invalid TZX Glue Block signature" << std::endl;
1413
0
        return pos + 9;
1414
0
    }
1415
1416
    // Read major and minor version numbers
1417
0
    uint8_t majorVersion = data[pos + 7];
1418
0
    uint8_t minorVersion = data[pos + 8];
1419
0
    pos += 9;
1420
1421
    // This block doesn't contain data for TapBlocks, so we just skip it
1422
0
    std::cout << "Skipping TZX Glue Block: version " << (int)majorVersion << "." << (int)minorVersion << std::endl;
1423
1424
0
    return pos;
1425
0
}
1426
1427
// Get number of parsed blocks
1428
size_t Tape::getBlockCount() const
1429
0
{
1430
0
    return tapBlocks.size();
1431
0
}
1432
1433
// Get a specific block
1434
const TapBlock &Tape::getBlock(size_t index) const
1435
0
{
1436
0
    static TapBlock emptyBlock; // Return empty block if index is out of bounds
1437
0
    if (index >= tapBlocks.size())
1438
0
    {
1439
0
        return emptyBlock;
1440
0
    }
1441
0
    return tapBlocks[index];
1442
0
}
1443
1444
// Get the bit stream for debugging
1445
const std::vector<TapeImpulse> &Tape::getBitStream() const
1446
0
{
1447
0
    return bitStream;
1448
0
}
1449
1450
// For testing purposes: set up a test bit stream
1451
void Tape::setTestBitStream(const std::vector<TapeImpulse> &testStream)
1452
0
{
1453
0
    bitStream = testStream;
1454
0
    currentImpulseIndex = 0;
1455
0
    currentImpulseTicks = 0;
1456
0
}
1457
1458
// Prepare bit stream from parsed blocks
1459
// This function generates a byte stream where each impulse is represented as (uint32_t ticks, bool value)
1460
void Tape::prepareBitStream()
1461
1
{
1462
    // Clear any existing bit stream
1463
1
    bitStream.clear();
1464
1465
    // Process each block and generate the corresponding impulses
1466
9
    for (size_t i = 0; i < tapBlocks.size(); ++i)
1467
8
    {
1468
8
        const TapBlock &block = tapBlocks[i];
1469
1470
        // Determine if this is a header or data block to set appropriate pilot tone length
1471
8
        uint pilotLength = (block.flag == 0x00) ? tapePilotLenHeader : tapePilotLenData;
1472
1473
        // Generate pilot tone
1474
        // For each impulse: value=1 for tapePilot ticks, then value=0 for tapePilot ticks
1475
1476
24.9k
        for (uint j = 0; j < pilotLength; ++j)
1477
24.8k
        {
1478
            // High impulse
1479
24.8k
            TapeImpulse highImpulse;
1480
24.8k
            highImpulse.ticks = tapePilot;
1481
24.8k
            highImpulse.value = true;
1482
24.8k
            bitStream.push_back(highImpulse);
1483
1484
            // Low impulse
1485
24.8k
            TapeImpulse lowImpulse;
1486
24.8k
            lowImpulse.ticks = tapePilot;
1487
24.8k
            lowImpulse.value = false;
1488
24.8k
            bitStream.push_back(lowImpulse);
1489
24.8k
        }
1490
1491
        // Generate sync pulses
1492
        // First sync pulse: tapeSync1 ticks with value=1
1493
8
        TapeImpulse sync1Impulse;
1494
8
        sync1Impulse.ticks = tapeSync1;
1495
8
        sync1Impulse.value = true;
1496
8
        bitStream.push_back(sync1Impulse);
1497
1498
        // Second sync pulse: tapeSync2 ticks with value=0
1499
8
        TapeImpulse sync2Impulse;
1500
8
        sync2Impulse.ticks = tapeSync2;
1501
8
        sync2Impulse.value = false;
1502
8
        bitStream.push_back(sync2Impulse);
1503
1504
        // Generate data bits
1505
        // For each byte in the block (including flag, data, and checksum):
1506
        //   - For each bit (MSB first):
1507
        //     - Generate bit impulse (tape0 for 0, tape1 for 1)
1508
        //     - Each impulse consists of value=1 then value=0
1509
1510
        // Process each byte directly from block.data
1511
42.5k
        for (size_t byteIndex = 0; byteIndex < block.data.size(); ++byteIndex)
1512
42.5k
        {
1513
42.5k
            uint8_t byte = block.data[byteIndex];
1514
            // printf("Parse %d %zu\n", i,byteIndex);
1515
            //  Process each bit (MSB first)
1516
383k
            for (int bitIndex = 7; bitIndex >= 0; --bitIndex)
1517
340k
            {
1518
                // for (int bitIndex = 0; bitIndex <7; bitIndex++)  {
1519
340k
                bool bitValue = (byte >> bitIndex) & 1;
1520
1521
                // Determine pulse length based on bit value
1522
340k
                uint pulseLength = bitValue ? tape1 : tape0;
1523
1524
                // Generate impulse: value=1 for pulseLength ticks
1525
340k
                TapeImpulse highImpulse;
1526
340k
                highImpulse.ticks = pulseLength;
1527
340k
                highImpulse.value = true;
1528
340k
                bitStream.push_back(highImpulse);
1529
1530
                // Generate impulse: value=0 for pulseLength ticks
1531
340k
                TapeImpulse lowImpulse;
1532
340k
                lowImpulse.ticks = pulseLength;
1533
340k
                lowImpulse.value = false;
1534
340k
                bitStream.push_back(lowImpulse);
1535
340k
            }
1536
42.5k
        }
1537
1538
8
        sync2Impulse.ticks = tapeFinalSync;
1539
8
        sync2Impulse.value = true;
1540
8
        bitStream.push_back(sync2Impulse);
1541
        // Generate pause between blocks
1542
        // Pause is all 0 for tapePilotPause length
1543
8
        TapeImpulse pauseImpulse;
1544
8
        pauseImpulse.ticks = tapePilotPause;
1545
8
        pauseImpulse.value = false;
1546
8
        bitStream.push_back(pauseImpulse);
1547
8
    }
1548
1
    printf("Consumed %lu bytes for bitStream\n", bitStream.size() * sizeof(TapeImpulse));
1549
1
}
1550
1551
// Get next audio input state for ULA
1552
bool Tape::getNextBit()
1553
942M
{
1554
    // If no tape played, return
1555
942M
    if (!isTapePlayed)
1556
0
        return false;
1557
    // If no bit stream has been generated, return false
1558
942M
    if (bitStream.empty())
1559
0
    {
1560
0
        isTapePlayed = false;
1561
0
        printf("TAPE STOP1\n");
1562
0
        return false;
1563
0
    }
1564
1565
    // If we've processed all impulses, return false (pause state)
1566
942M
    if (currentImpulseIndex >= bitStream.size())
1567
1
    {
1568
1
        isTapePlayed = false;
1569
1
        printf("TAPE STOP2\n");
1570
1
        return false;
1571
1
    }
1572
1573
    // Get the current impulse
1574
942M
    const TapeImpulse &currentImpulse = bitStream[currentImpulseIndex];
1575
1576
    // Increment the tick counter for the current impulse
1577
942M
    currentImpulseTicks++;
1578
1579
    // Check if we've exhausted the current impulse
1580
942M
    if (currentImpulseTicks >= currentImpulse.ticks)
1581
730k
    {
1582
        // Move to the next impulse
1583
730k
        currentImpulseIndex++;
1584
730k
        currentImpulseTicks = 0;
1585
730k
    }
1586
    // printf("%d ",currentImpulseIndex);
1587
    //  Return the value of the current impulse
1588
942M
    return currentImpulse.value;
1589
942M
}
/Users/kiltum/projects/zxcpp/src/ula.cpp
Line
Count
Source
1
#include "ula.hpp"
2
#include <cstring>
3
#include <algorithm>
4
#include <iostream>
5
6
// Constructor
7
ULA::ULA(Memory *mem, Tape *tap)
8
1
{
9
1
    memory = mem;
10
1
    tape = tap;
11
    // Allocate screen buffer (352x288 to accommodate borders)
12
1
    screenBuffer = new uint32_t[(352 * 288) + 2]; // +2 to prevent buffer overflow
13
14
    // Initialize state variables
15
1
    clock = 0;
16
1
    line = 0;
17
1
    flash = false;
18
1
    flashCnt = 0;
19
1
    frameCnt = 0;
20
1
    borderColor = 0;
21
1
    horClock = 0;
22
1
    audioState = false;
23
24
    // Initialize keyboard state (all keys released)
25
9
    for (int i = 0; i < 8; i++)
26
8
    {
27
8
        keyboard[i] = 0xFF; // All bits set = all keys released
28
8
    }
29
30
1
    colors[0] = 0xFF000000;  // Black
31
1
    colors[1] = 0xFF0000C0;  // Blue
32
1
    colors[2] = 0xFFC00000;  // Red
33
1
    colors[3] = 0xFFC000C0;  // Magenta
34
1
    colors[4] = 0xFF00C000;  // Green
35
1
    colors[5] = 0xFF00C0C0;  // Cyan
36
1
    colors[6] = 0xFFC0C000;  // Yellow
37
1
    colors[7] = 0xFFC0C0C0;  // White
38
1
    colors[8] = 0xFF000000;  // Black (bright)
39
1
    colors[9] = 0xFF0000FF;  // Bright Blue
40
1
    colors[10] = 0xFFFF0000; // Bright Red
41
1
    colors[11] = 0xFFFF00FF; // Bright Magenta
42
1
    colors[12] = 0xFF00FF00; // Bright Green
43
1
    colors[13] = 0xFFFFFF00; // Bright Cyan
44
1
    colors[14] = 0xFF00FFFF; // Bright Yellow
45
1
    colors[15] = 0xFFFFFFFF; // Bright White
46
47
101k
    for (int i = 0; i < 352 * 288; i++)
48
101k
    {
49
101k
        screenBuffer[i] = colors[0];
50
101k
    }
51
1
    change48(true);
52
1
}
53
54
// Destructor
55
ULA::~ULA()
56
1
{
57
1
    delete[] screenBuffer;
58
1
}
59
60
// Read a byte from the specified port
61
uint8_t ULA::readPort(uint16_t port)
62
9.95M
{
63
    // ULA handles port 0xFE for keyboard input and other functions
64
9.95M
    if ((port & 0xFF) == 0xFE)
65
9.95M
    {
66
        // Extract the half-row selection from bits 8-15 of the port address
67
9.95M
        uint8_t halfRowSelect = (port >> 8) & 0xFF;
68
69
        // Find the first zero bit in the half-row selection (active low)
70
        // This determines which half-row to read
71
79.6M
        for (int i = 0; i < 8; i++)
72
79.6M
        {
73
79.6M
            if ((halfRowSelect & (1 << i)) == 0)
74
9.95M
            {
75
                // Return the state of the selected half-row
76
                // 0 = key pressed, 1 = key released (inverted logic)
77
9.95M
                uint8_t result = keyboard[i];
78
79
                // Set bit 6 based on audioState
80
9.95M
                if (audioState)
81
4.46M
                {
82
4.46M
                    result |= 0x40; // Set bit 6
83
4.46M
                }
84
5.48M
                else
85
5.48M
                {
86
5.48M
                    result &= ~0x40; // Clear bit 6
87
5.48M
                }
88
89
                // printf("RA1 %d\n",audioState);
90
9.95M
                return result;
91
9.95M
            }
92
79.6M
        }
93
94
        // If no half-row is selected, return all keys released
95
0
        uint8_t result = 0xFF;
96
97
0
        return result;
98
9.95M
    }
99
100
0
    return 0;
101
9.95M
}
102
103
// Write a byte to the specified port
104
void ULA::writePort(uint16_t port, uint8_t value)
105
717k
{
106
    // printf("port %d", port);
107
717k
    if ((port & 0xFF) == 0xFE)
108
717k
    {
109
717k
        borderColor = value & 0x07;
110
717k
        bool earBit = (value & 0x10) != 0; // EAR is bit 4 (0x10) - active high
111
112
717k
        audioState = earBit; // This is stub for tests. Actual sound handling in sound.cpp
113
717k
    }
114
717k
}
115
116
void ULA::change48(bool is48)
117
2
{
118
2
    if (is48)
119
2
    {
120
2
        clockFlyback = 3560;
121
2
        clockEndFrame = 69888;
122
2
        clockBottomRight = 68072;
123
2
        clockPerLine = 224;
124
2
    }
125
0
    else
126
0
    {
127
0
        clockFlyback = 3368;
128
0
        clockEndFrame = 70908;
129
0
        clockBottomRight = 69032;
130
0
        clockPerLine = 228;
131
0
    }
132
2
}
133
134
// Get screen buffer
135
uint32_t *ULA::getScreenBuffer()
136
4.46k
{
137
4.46k
    return screenBuffer;
138
4.46k
}
139
// 48 version
140
// 3560 - flyback
141
// *----------------------------* 48*224 = 10752
142
// *   V48                      *
143
// *  +----------------------*  * 3560 + 10752 + 24 = 14336 till first byte
144
// *  !V192                  !  *
145
// *24!     H128             !24* - 48 retrace = 224
146
// *  !                      !  * 192 * 224 = 43008
147
// *  +----------------------+  * 14336 + 43008 = 57344 till right corner
148
// *   v48                      * 48 * 224 = 10752
149
// *----------------------------* 68072 on bottom right corner
150
// 1816 overscan.
151
// 3560 + 10752 + 43008 + 10752 + 1816 = 69888
152
// Visible Width = (24+128+24) * 2 = 352
153
// Visible Height = (48+192+48) = 288
154
155
// 128 version
156
// 3368 - flyback
157
// *----------------------------* 48*228 = 10944
158
// *   V48                      *
159
// *  +----------------------*  * 3368 + 10944 + 24 = 14336 till first byte
160
// *  !V192                  !  *
161
// *24!     H128             !24* - 52 retrace = 228 (!)
162
// *  !                      !  * 192 * 228 = 43776
163
// *  +----------------------+  * 14336 + 43776 = 58112 till right corner
164
// *   v48                      * 48 * 228 = 10944
165
// *----------------------------* 69032 on brc
166
// 1876 overscan.
167
// 3368 + 10944 + 43776 + 10944 + 1876 = 70908
168
169
// Process a single ULA tick
170
// Returns internal clock state.
171
int ULA::oneTick()
172
1.24G
{
173
    // Process one clock tick
174
1.24G
    clock++;
175
176
1.24G
    if (tape->isTapePlayed)
177
942M
    { // if tape is playing something, set input bit
178
942M
        audioState = tape->getNextBit();
179
942M
    }
180
181
1.24G
    if (clock <= clockFlyback)
182
63.4M
    { // we are on flyback
183
63.4M
        return clock;
184
63.4M
    }
185
186
1.18G
    if (clock > clockBottomRight && clock < clockEndFrame)
187
32.3M
    { // we are on over scan
188
32.3M
        return clock;
189
32.3M
    }
190
191
    // Check if we've completed a frame
192
1.14G
    if (clock >= clockEndFrame)
193
17.8k
    {
194
        // Reset counters for next frame
195
17.8k
        clock = 0;
196
17.8k
        line = 0;
197
198
        // Increment frame counter for flash timing
199
17.8k
        frameCnt++;
200
201
        // Handle flash counter (every 16 frames)
202
17.8k
        if (frameCnt >= 16)
203
1.11k
        {
204
1.11k
            flash = !flash;
205
1.11k
            flashCnt = (flashCnt + 1) & 0x0F;
206
1.11k
            frameCnt = 0;
207
1.11k
        }
208
17.8k
        return 0;
209
17.8k
    }
210
211
1.14G
    line = (clock - clockFlyback) / clockPerLine;
212
213
    // now lets draw screen
214
1.14G
    if (horClock <= 24)
215
128M
    { // beam on left border
216
128M
        drawPixel(borderColor);
217
128M
    }
218
1.14G
    if (horClock > 23 && horClock <= (24 + 128 - 1))
219
656M
    {
220
656M
        if (line <= 47 || line >= (192 + 48)) // its up border, still no need to access screen
221
218M
        {
222
218M
            drawPixel(borderColor);
223
218M
        }
224
437M
        else
225
437M
        {
226
437M
            int x = (horClock - 24) * 2;
227
437M
            int y = line - 48;
228
            // printf("%d %d\n", x, y);
229
437M
            screenBuffer[(y + 48) * 352 + x + 48] = getPixelColorFast(x, y);
230
437M
            screenBuffer[(y + 48) * 352 + x + 48 + 1] = getPixelColorFast(x + 1, y);
231
            // screenBuffer[(y + 48) * 352 + x + 48] = colors[2];
232
            // screenBuffer[(y + 48) * 352 + x + 48 + 1] = colors[0];
233
437M
        }
234
656M
    }
235
236
1.14G
    if (horClock >= (24 + 128) && horClock <= (24 + 128 + 24))
237
128M
    { // beam on right border
238
128M
        drawPixel(borderColor);
239
128M
    }
240
241
1.14G
    horClock++;
242
1.14G
    if (horClock > (clockPerLine - 1))
243
5.13M
    { // beam end line and return back
244
5.13M
        horClock = 0;
245
        // printf("---- %d %d \n", clock, clock-3560);
246
5.13M
    }
247
248
    // printf("%d %d %d \n", clock, horClock, line);
249
1.14G
    return clock;
250
1.14G
}
251
252
// Draw the current pixel
253
void ULA::drawPixel(int color)
254
475M
{
255
    // printf("%d %d \n", line, horClock);
256
    //  One tick = 2 pixel to draw
257
475M
    screenBuffer[line * 352 + (horClock * 2 + 1)] = colors[color];
258
475M
    screenBuffer[line * 352 + horClock * 2] = colors[color];
259
475M
}
260
261
// Get pixel color based on ZX Spectrum video memory layout
262
uint32_t ULA::getPixelColorFast(uint8_t x, uint8_t y)
263
875M
{
264
    // ZX Spectrum video memory layout:
265
    // - Screen bitmap: 0x4000-0x57FF (6144 bytes)
266
    // - Attributes: 0x5800-0x5AFF (768 bytes)
267
268
    // Calculate bitmap address
269
    // Formula: 0x4000 + (y & 0xC0) << 5 + (y & 0x07) << 8 + (y & 0x38) << 2 + (x >> 3)
270
875M
    uint16_t addr = 0x4000 + ((y & 0xC0) << 5) + ((y & 0x07) << 8) + ((y & 0x38) << 2) + (x >> 3);
271
272
    // Read bitmap byte
273
875M
    uint8_t bitmap = memory->ULAReadByte(addr);
274
275
    // Calculate attribute address
276
    // Formula: 0x5800 + (y >> 3) * 32 + (x >> 3)
277
875M
    uint16_t attrAddr = 0x5800 + ((y >> 3) * 32) + (x >> 3);
278
279
    // Read attribute byte
280
    // Bits 0-2: Ink color
281
    // Bits 3-5: Paper color
282
    // Bit 6: Bright flag
283
    // Bit 7: Flash flag
284
875M
    uint8_t attr = memory->ULAReadByte(attrAddr);
285
286
    // Get bit value for this pixel
287
875M
    uint8_t bit = 0x80 >> (x & 0x07);
288
289
    // Determine ink and paper colors
290
875M
    uint8_t ink, paper;
291
875M
    if ((attr & 0x80) != 0 && flash)
292
0
    {                             // Flash bit set and flash active
293
0
        ink = (attr >> 3) & 0x07; // Paper color as ink
294
0
        paper = attr & 0x07;      // Ink color as paper
295
0
    }
296
875M
    else
297
875M
    {
298
875M
        ink = attr & 0x07;          // Normal ink color
299
875M
        paper = (attr >> 3) & 0x07; // Normal paper color
300
875M
    }
301
302
    // Apply bright flag
303
875M
    if ((attr & 0x40) != 0)
304
548M
    {
305
548M
        ink |= 0x08;
306
548M
        paper |= 0x08;
307
548M
    }
308
309
    // Select color based on bit value
310
875M
    uint8_t colorIndex;
311
875M
    if ((bitmap & bit) != 0)
312
122M
    {
313
122M
        colorIndex = ink;
314
122M
    }
315
753M
    else
316
753M
    {
317
753M
        colorIndex = paper;
318
753M
    }
319
320
    // Return pre-calculated color
321
875M
    return colors[colorIndex];
322
875M
}
323
324
// Reset ULA state
325
void ULA::reset()
326
0
{
327
0
    clock = 0;
328
0
    line = 0;
329
0
    flash = false;
330
0
    flashCnt = 0;
331
0
    frameCnt = 0;
332
0
    borderColor = 0;
333
334
    // Reinitialize screen with black border
335
0
    uint32_t blackColor = colors[0];
336
0
    for (int i = 0; i < 320 * 240; i++)
337
0
    {
338
0
        screenBuffer[i] = blackColor;
339
0
    }
340
341
    // Reinitialize keyboard state (all keys released)
342
0
    for (int i = 0; i < 8; i++)
343
0
    {
344
0
        keyboard[i] = 0xFF; // All bits set = all keys released
345
0
    }
346
0
}
347
348
// Set the state of a key in the keyboard matrix
349
void ULA::setKeyState(int halfRow, uint8_t keyMask)
350
0
{
351
0
    if (halfRow >= 0 && halfRow < 8)
352
0
    {
353
0
        keyboard[halfRow] = keyMask;
354
0
    }
355
0
}
356
357
// Set a key as pressed (0 = pressed)
358
void ULA::setKeyDown(int halfRow, int keyBit)
359
3
{
360
3
    if (halfRow >= 0 && halfRow < 8 && keyBit >= 0 && keyBit < 8)
361
3
    {
362
3
        keyboard[halfRow] &= ~(1 << keyBit); // Clear bit to press key (0 = pressed)
363
3
    }
364
3
}
365
366
// Set a key as released (1 = released)
367
void ULA::setKeyUp(int halfRow, int keyBit)
368
3
{
369
3
    if (halfRow >= 0 && halfRow < 8 && keyBit >= 0 && keyBit < 8)
370
3
    {
371
3
        keyboard[halfRow] |= (1 << keyBit); // Set bit to release key (1 = released)
372
3
    }
373
3
}
/Users/kiltum/projects/zxcpp/src/z80.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
#include "port.hpp"
4
5
Z80::Z80(Memory *mem, Port *port)
6
1
{
7
    // Store the memory and port pointers
8
1
    memory = mem;
9
1
    this->port = port;
10
11
    // Initialize main registers to zero
12
1
    AF = 0;
13
1
    BC = 0;
14
1
    DE = 0;
15
1
    HL = 0;
16
17
    // Initialize shadow registers to zero
18
1
    AF_ = 0;
19
1
    BC_ = 0;
20
1
    DE_ = 0;
21
1
    HL_ = 0;
22
23
    // Initialize index registers to zero
24
1
    IX = 0;
25
1
    IY = 0;
26
27
    // Initialize internal registers to zero
28
1
    SP = 0xFFFF;
29
1
    PC = 0;
30
1
    I = 0;
31
1
    R = 0;
32
1
    MEMPTR = 0;
33
34
    // Initialize interrupt-related registers and flags
35
1
    IFF1 = false;
36
1
    IFF2 = false;
37
1
    IM = 0;
38
1
    HALT = false;
39
1
    InterruptPending = false;
40
1
    isNMOS = true;
41
1
}
42
43
int Z80::ExecuteOneInstruction()
44
168M
{
45
    // Handle interrupts first if enabled
46
168M
    if (IFF1 && InterruptPending)
47
65
    {
48
65
        InterruptPending = false;
49
65
        return HandleInterrupt();
50
65
    }
51
168M
    InterruptPending = false;
52
53
    // Handle HALT state
54
168M
    if (HALT)
55
0
    {
56
0
        return 4; // 4 T-states for HALT
57
0
    }
58
59
    // Read the first opcode byte
60
168M
    uint8_t opcode = memory->ReadByte(PC);
61
62
    // Handle prefix opcodes
63
168M
    switch (opcode)
64
168M
    {
65
775k
    case 0xDD: // DD prefix (IX instructions)
66
        // Increment PC past the prefix
67
775k
        PC++;
68
775k
        return ExecuteDDOpcode();
69
70
99.1k
    case 0xFD: // FD prefix (IY instructions)
71
        // Increment PC past the prefix
72
99.1k
        PC++;
73
99.1k
        return ExecuteFDOpcode();
74
75
1.70M
    case 0xCB: // CB prefix (bit manipulation instructions)
76
        // Increment PC past the prefix
77
1.70M
        PC++;
78
1.70M
        return ExecuteCBOpcode();
79
80
2.24M
    case 0xED: // ED prefix (extended instructions)
81
        // Increment PC past the prefix
82
2.24M
        PC++;
83
2.24M
        return ExecuteEDOpcode();
84
85
163M
    default: // Regular opcode
86
163M
        return ExecuteOpcode();
87
168M
    }
88
168M
}
89
90
void Z80::NMI()
91
0
{
92
0
    IFF2 = IFF1;
93
0
    IFF1 = false;
94
0
    Push(PC);
95
0
    PC = 0x0066;
96
0
}
97
98
// HandleInterrupt handles interrupt processing
99
int Z80::HandleInterrupt()
100
65
{
101
    // Exit HALT state
102
    // The HALT instruction halts the Z80; it does not increase the PC so that the instruction is re-
103
    // executed, until a maskable or non-maskable interrupt is accepted. Only then does the Z80 increase
104
    // the PC again and continues with the next instruction. During the HALT state, the HALT line is
105
    // set. The PC is increased before the interrupt routine is called.
106
65
    if (HALT)
107
0
    {
108
0
        HALT = false;
109
0
        PC++; // Increment PC to exit HALT state
110
0
    }
111
112
    // Reset interrupt flip-flops
113
65
    IFF1 = false;
114
65
    IFF2 = false;
115
116
    // Handle interrupt based on mode
117
65
    switch (IM)
118
65
    {
119
0
    case 0:
120
65
    case 1:
121
        // Mode 0/1: Restart at address 0x0038
122
65
        Push(PC);
123
65
        PC = 0x0038;
124
65
        return 13; // 13 T-states for interrupt handling
125
126
0
    case 2:
127
        // Mode 2: Call interrupt vector
128
0
        Push(PC);
129
0
        {
130
0
            uint16_t vectorAddr = (uint16_t(I) << 8) | 0xFF; // Use 0xFF as vector for non-maskable interrupt
131
0
            PC = (uint16_t(memory->ReadByte(vectorAddr + 1)) << 8) | uint16_t(memory->ReadByte(vectorAddr));
132
0
        }
133
0
        return 19; // 19 T-states for interrupt handling
134
135
0
    default:
136
        // Should not happen, but handle gracefully
137
0
        return 0;
138
65
    }
139
65
}
140
141
// UpdateSZFlags updates the S and Z flags based on an 8-bit result
142
void Z80::UpdateSZFlags(uint8_t result)
143
1.56M
{
144
1.56M
    SetFlag(FLAG_S, (result & 0x80) != 0);
145
1.56M
    SetFlag(FLAG_Z, result == 0);
146
1.56M
}
147
148
// UpdatePVFlags updates the P/V flag based on an 8-bit result (parity calculation)
149
void Z80::UpdatePVFlags(uint8_t result)
150
0
{
151
    // Calculate parity (even number of 1-bits = 1, odd = 0)
152
0
    uint8_t parity = 1;
153
0
    for (int i = 0; i < 8; i++)
154
0
    {
155
0
        parity ^= (result >> i) & 1;
156
0
    }
157
0
    SetFlag(FLAG_PV, parity != 0);
158
0
}
159
160
// UpdateSZXYPVFlags updates the S, Z, X, Y, P/V flags based on an 8-bit result
161
void Z80::UpdateSZXYPVFlags(uint8_t result)
162
816k
{
163
816k
    SetFlag(FLAG_S, (result & 0x80) != 0);
164
816k
    SetFlag(FLAG_Z, result == 0);
165
816k
    SetFlag(FLAG_X, (result & FLAG_X) != 0);
166
816k
    SetFlag(FLAG_Y, (result & FLAG_Y) != 0);
167
168
    // Calculate parity (even number of 1-bits = 1, odd = 0)
169
816k
    uint8_t parity = 1;
170
7.34M
    for (int i = 0; i < 8; i++)
171
6.52M
    {
172
6.52M
        parity ^= (result >> i) & 1;
173
6.52M
    }
174
816k
    SetFlag(FLAG_PV, parity != 0);
175
816k
}
176
177
// UpdateFlags3and5FromValue updates the X and Y flags from an 8-bit value
178
void Z80::UpdateFlags3and5FromValue(uint8_t value)
179
1.56M
{
180
1.56M
    SetFlag(FLAG_X, (value & FLAG_X) != 0);
181
1.56M
    SetFlag(FLAG_Y, (value & FLAG_Y) != 0);
182
1.56M
}
183
184
// UpdateFlags3and5FromAddress updates the X and Y flags from the high byte of an address
185
void Z80::UpdateFlags3and5FromAddress(uint16_t address)
186
1.01M
{
187
1.01M
    SetFlag(FLAG_X, ((address >> 8) & FLAG_X) != 0);
188
1.01M
    SetFlag(FLAG_Y, ((address >> 8) & FLAG_Y) != 0);
189
1.01M
}
190
191
// UpdateSZXYFlags updates the S, Z, X, Y flags based on an 8-bit result
192
void Z80::UpdateSZXYFlags(uint8_t result)
193
42.8M
{
194
42.8M
    SetFlag(FLAG_S, (result & 0x80) != 0);
195
42.8M
    SetFlag(FLAG_Z, result == 0);
196
42.8M
    SetFlag(FLAG_X, (result & FLAG_X) != 0);
197
42.8M
    SetFlag(FLAG_Y, (result & FLAG_Y) != 0);
198
42.8M
}
199
200
// UpdateXYFlags updates the undocumented X and Y flags based on an 8-bit result
201
void Z80::UpdateXYFlags(uint8_t result)
202
11.1M
{
203
11.1M
    SetFlag(FLAG_X, (result & FLAG_X) != 0);
204
11.1M
    SetFlag(FLAG_Y, (result & FLAG_Y) != 0);
205
11.1M
}
206
207
// GetFlag returns the state of a specific flag
208
bool Z80::GetFlag(uint8_t flag)
209
47.9M
{
210
47.9M
    return (F & flag) != 0;
211
47.9M
}
212
213
// SetFlag sets a flag to a specific state
214
void Z80::SetFlag(uint8_t flag, bool state)
215
412M
{
216
412M
    if (state)
217
128M
    {
218
128M
        F |= flag;
219
128M
    }
220
284M
    else
221
284M
    {
222
284M
        F &= ~flag;
223
284M
    }
224
412M
}
225
226
// ClearFlag clears a specific flag
227
void Z80::ClearFlag(uint8_t flag)
228
6.25M
{
229
6.25M
    F &= ~flag;
230
6.25M
}
231
232
// ClearAllFlags clears all flags
233
void Z80::ClearAllFlags()
234
0
{
235
0
    F = 0;
236
0
}
237
238
// ReadImmediateByte reads the next byte from memory at PC and increments PC
239
uint8_t Z80::ReadImmediateByte()
240
37.1M
{
241
37.1M
    uint8_t value = memory->ReadByte(PC);
242
37.1M
    PC++;
243
37.1M
    return value;
244
37.1M
}
245
246
// ReadImmediateWord reads the next word from memory at PC and increments PC by 2
247
uint16_t Z80::ReadImmediateWord()
248
8.37M
{
249
8.37M
    uint8_t lo = memory->ReadByte(PC);
250
8.37M
    PC++;
251
8.37M
    uint8_t hi = memory->ReadByte(PC);
252
8.37M
    PC++;
253
8.37M
    return (uint16_t(hi) << 8) | uint16_t(lo);
254
8.37M
}
255
256
// ReadDisplacement reads an 8-bit signed displacement value
257
int8_t Z80::ReadDisplacement()
258
25.8M
{
259
25.8M
    int8_t value = int8_t(memory->ReadByte(PC));
260
25.8M
    PC++;
261
25.8M
    return value;
262
25.8M
}
263
264
// ReadOpcode reads the next opcode from memory at PC and increments PC
265
uint8_t Z80::ReadOpcode()
266
168M
{
267
168M
    uint8_t opcode = memory->ReadByte(PC);
268
168M
    PC++;
269
    // Increment R register (memory refresh) for each opcode fetch
270
    // Note: R is a 7-bit register, bit 7 remains unchanged
271
168M
    R = (R & 0x80) | ((R + 1) & 0x7F);
272
168M
    return opcode;
273
168M
}
274
275
// Push pushes a 16-bit value onto the stack
276
void Z80::Push(uint16_t value)
277
2.18M
{
278
2.18M
    SP -= 2;
279
2.18M
    memory->WriteByte(SP, uint8_t(value & 0xFF));
280
2.18M
    memory->WriteByte(SP + 1, uint8_t((value >> 8) & 0xFF));
281
2.18M
}
282
283
// Pop pops a 16-bit value from the stack
284
uint16_t Z80::Pop()
285
3.18M
{
286
    // Read low byte first, then high byte (little-endian)
287
3.18M
    uint8_t lo = memory->ReadByte(SP);
288
3.18M
    uint8_t hi = memory->ReadByte(SP + 1);
289
3.18M
    SP += 2;
290
3.18M
    return (uint16_t(hi) << 8) | uint16_t(lo);
291
3.18M
}
292
293
// inc8 increments an 8-bit value and updates flags
294
uint8_t Z80::inc8(uint8_t value)
295
12.8M
{
296
12.8M
    uint8_t result = value + 1;
297
12.8M
    SetFlag(FLAG_H, (value & 0x0F) == 0x0F);
298
12.8M
    SetFlag(FLAG_N, false);
299
12.8M
    UpdateSZXYFlags(result);
300
    // Set PV flag if incrementing 0x7F to 0x80 (overflow from positive to negative)
301
12.8M
    SetFlag(FLAG_PV, value == 0x7F);
302
12.8M
    return result;
303
12.8M
}
304
305
// dec8 decrements an 8-bit value and updates flags
306
uint8_t Z80::dec8(uint8_t value)
307
2.69M
{
308
2.69M
    uint8_t result = value - 1;
309
2.69M
    SetFlag(FLAG_H, (value & 0x0F) == 0x00);
310
2.69M
    SetFlag(FLAG_N, true);
311
2.69M
    UpdateSZXYFlags(result);
312
    // Set PV flag if decrementing 0x80 to 0x7F (overflow from negative to positive)
313
2.69M
    SetFlag(FLAG_PV, value == 0x80);
314
2.69M
    return result;
315
2.69M
}
316
317
// rlca rotates the accumulator left circular
318
void Z80::rlca()
319
3.24k
{
320
3.24k
    uint8_t result = (A << 1) | (A >> 7);
321
3.24k
    A = result;
322
3.24k
    SetFlag(FLAG_C, (A & 0x01) != 0);
323
3.24k
    SetFlag(FLAG_H, false);
324
3.24k
    SetFlag(FLAG_N, false);
325
3.24k
    UpdateXYFlags(A);
326
3.24k
}
327
328
// rla rotates the accumulator left through carry
329
void Z80::rla()
330
54.9k
{
331
54.9k
    bool oldCarry = GetFlag(FLAG_C);
332
54.9k
    uint8_t result = (A << 1);
333
54.9k
    if (oldCarry)
334
19.7k
    {
335
19.7k
        result |= 0x01;
336
19.7k
    }
337
54.9k
    SetFlag(FLAG_C, (A & 0x80) != 0);
338
54.9k
    A = result;
339
54.9k
    SetFlag(FLAG_H, false);
340
54.9k
    SetFlag(FLAG_N, false);
341
54.9k
    UpdateXYFlags(A);
342
54.9k
}
343
344
// rrca rotates the accumulator right circular
345
void Z80::rrca()
346
409k
{
347
409k
    uint8_t result = (A >> 1) | (A << 7);
348
409k
    A = result;
349
409k
    SetFlag(FLAG_C, (A & 0x80) != 0);
350
409k
    SetFlag(FLAG_H, false);
351
409k
    SetFlag(FLAG_N, false);
352
409k
    UpdateXYFlags(A);
353
409k
}
354
355
// rra rotates the accumulator right through carry
356
void Z80::rra()
357
9.96M
{
358
9.96M
    bool oldCarry = GetFlag(FLAG_C);
359
9.96M
    uint8_t result = (A >> 1);
360
9.96M
    if (oldCarry)
361
1.39k
    {
362
1.39k
        result |= 0x80;
363
1.39k
    }
364
9.96M
    SetFlag(FLAG_C, (A & 0x01) != 0);
365
9.96M
    A = result;
366
9.96M
    SetFlag(FLAG_H, false);
367
9.96M
    SetFlag(FLAG_N, false);
368
9.96M
    UpdateXYFlags(A);
369
9.96M
}
370
371
// daa performs decimal adjust on accumulator
372
void Z80::daa()
373
0
{
374
0
    uint8_t correction = 0;
375
376
0
    if (GetFlag(FLAG_H) || (A & 0x0F) > 9)
377
0
    {
378
0
        correction += 0x06;
379
0
    }
380
381
0
    if (A > 0x99 || GetFlag(FLAG_C))
382
0
    {
383
0
        correction += 0x60;
384
0
        SetFlag(FLAG_C, true);
385
0
    }
386
387
0
    bool isSubtraction = GetFlag(FLAG_N);
388
0
    if (isSubtraction)
389
0
    {
390
0
        SetFlag(FLAG_H, GetFlag(FLAG_H) && (A & 0x0F) < 0x06);
391
0
        A -= correction;
392
0
    }
393
0
    else
394
0
    {
395
0
        SetFlag(FLAG_H, (A & 0x0F) > 9);
396
0
        A += correction;
397
0
    }
398
399
0
    SetFlag(FLAG_S, (A & 0x80) != 0);
400
0
    SetFlag(FLAG_Z, A == 0);
401
0
    SetFlag(FLAG_PV, parity(A));
402
0
    SetFlag(FLAG_X, (A & FLAG_X) != 0);
403
0
    SetFlag(FLAG_Y, (A & FLAG_Y) != 0);
404
0
}
405
406
// Helper function to calculate parity
407
bool Z80::parity(uint8_t val)
408
25.9M
{
409
25.9M
    int count = 0;
410
233M
    for (int i = 0; i < 8; i++)
411
207M
    {
412
207M
        if (val & (1 << i))
413
44.6M
        {
414
44.6M
            count++;
415
44.6M
        }
416
207M
    }
417
25.9M
    return (count % 2) == 0;
418
25.9M
}
419
420
// cpl complements the accumulator
421
void Z80::cpl()
422
726k
{
423
726k
    A = ~A;
424
726k
    SetFlag(FLAG_H, true);
425
726k
    SetFlag(FLAG_N, true);
426
726k
    UpdateXYFlags(A);
427
726k
}
428
429
// scf sets the carry flag
430
void Z80::scf()
431
718k
{
432
718k
    SetFlag(FLAG_C, true);
433
718k
    SetFlag(FLAG_N, false);
434
718k
    SetFlag(FLAG_H, false);
435
718k
    if (isNMOS)
436
0
    {
437
0
        SetFlag(FLAG_X, (A & FLAG_X) != 0);
438
0
        SetFlag(FLAG_Y, (A & FLAG_Y) != 0);
439
0
    }
440
718k
    else
441
718k
    {
442
        // This sets both flags if BOTH bits 3 and 5 are set in A, otherwise clears both
443
718k
        if ((A & FLAG_Y) == FLAG_Y && (A & FLAG_X) == FLAG_X)
444
35
        {
445
35
            SetFlag(FLAG_Y, true);
446
35
            SetFlag(FLAG_X, true);
447
35
        }
448
718k
    }
449
718k
}
450
451
// ccf complements the carry flag
452
void Z80::ccf()
453
56
{
454
56
    bool oldCarry = GetFlag(FLAG_C);
455
56
    SetFlag(FLAG_C, !oldCarry);
456
56
    SetFlag(FLAG_H, oldCarry); // H = old C
457
56
    SetFlag(FLAG_N, false);
458
56
    if (isNMOS)
459
0
    {
460
0
        SetFlag(FLAG_X, (A & FLAG_X) != 0);
461
0
        SetFlag(FLAG_Y, (A & FLAG_Y) != 0);
462
0
    }
463
56
    else
464
56
    {
465
56
        if ((A & FLAG_Y) == FLAG_Y && (A & FLAG_X) == FLAG_X)
466
2
        {
467
2
            SetFlag(FLAG_Y, true);
468
2
            SetFlag(FLAG_X, true);
469
2
        }
470
56
    }
471
56
}
472
473
// add16 adds two 16-bit values and updates flags
474
uint16_t Z80::add16(uint16_t a, uint16_t b)
475
858k
{
476
858k
    uint32_t result = (uint32_t)a + (uint32_t)b;
477
858k
    SetFlag(FLAG_C, result > 0xFFFF);
478
858k
    SetFlag(FLAG_H, (a & 0x0FFF) + (b & 0x0FFF) > 0x0FFF);
479
858k
    SetFlag(FLAG_N, false);
480
858k
    UpdateFlags3and5FromAddress((uint16_t)result);
481
858k
    return (uint16_t)result;
482
858k
}
483
484
// add8 adds an 8-bit value to the accumulator and updates flags
485
void Z80::add8(uint8_t value)
486
529k
{
487
529k
    uint8_t a = A;
488
529k
    uint16_t result = (uint16_t)a + (uint16_t)value;
489
529k
    SetFlag(FLAG_C, result > 0xFF);
490
529k
    SetFlag(FLAG_H, (a & 0x0F) + (value & 0x0F) > 0x0F);
491
529k
    SetFlag(FLAG_N, false);
492
529k
    UpdateSZXYFlags((uint8_t)result);
493
    // Set overflow flag: overflow occurs if adding two numbers with the same sign
494
    // produces a result with a different sign
495
    // Both operands have the same sign (both positive or both negative)
496
    // but the result has a different sign
497
529k
    bool sameSign = ((a ^ value) & 0x80) == 0;
498
529k
    bool differentResultSign = ((a ^ result) & 0x80) != 0;
499
529k
    bool overflow = sameSign && differentResultSign;
500
529k
    SetFlag(FLAG_PV, overflow);
501
529k
    A = (uint8_t)result;
502
529k
}
503
504
// adc8 adds an 8-bit value and carry to the accumulator and updates flags
505
void Z80::adc8(uint8_t value)
506
45
{
507
45
    uint8_t a = A;
508
45
    bool carry = GetFlag(FLAG_C);
509
45
    uint16_t result;
510
45
    if (carry)
511
12
    {
512
12
        result = (uint16_t)a + (uint16_t)value + 1;
513
12
    }
514
33
    else
515
33
    {
516
33
        result = (uint16_t)a + (uint16_t)value;
517
33
    }
518
45
    SetFlag(FLAG_C, (int)a + (int)value + (int)(carry ? 1 : 0) > 0xFF);
519
45
    SetFlag(FLAG_H, (a & 0x0F) + (value & 0x0F) + (carry ? 1 : 0) > 0x0F);
520
45
    SetFlag(FLAG_N, false);
521
45
    UpdateSZXYFlags((uint8_t)result);
522
    // Set overflow flag: overflow occurs if adding two numbers with the same sign
523
    // produces a result with a different sign
524
45
    uint8_t originalValue = a;
525
45
    if (carry)
526
12
    {
527
12
        bool sameSign = ((originalValue ^ value) & 0x80) == 0;
528
12
        bool differentResultSign = ((originalValue ^ result) & 0x80) != 0;
529
12
        bool overflow = sameSign && differentResultSign;
530
12
        SetFlag(FLAG_PV, overflow);
531
12
    }
532
33
    else
533
33
    {
534
33
        bool sameSign = ((originalValue ^ value) & 0x80) == 0;
535
33
        bool differentResultSign = ((originalValue ^ result) & 0x80) != 0;
536
33
        bool overflow = sameSign && differentResultSign;
537
33
        SetFlag(FLAG_PV, overflow);
538
33
    }
539
45
    A = (uint8_t)result;
540
45
}
541
542
// sub8 subtracts an 8-bit value from the accumulator and updates flags
543
void Z80::sub8(uint8_t value)
544
795k
{
545
795k
    uint8_t a = A;
546
795k
    uint16_t result = (uint16_t)a - (uint16_t)value;
547
795k
    SetFlag(FLAG_C, a < value);
548
795k
    SetFlag(FLAG_H, (a & 0x0F) < (value & 0x0F));
549
795k
    SetFlag(FLAG_N, true);
550
795k
    UpdateSZXYFlags((uint8_t)result);
551
    // Set overflow flag: overflow occurs if subtracting two numbers with different signs
552
    // produces a result with the same sign as the subtrahend
553
795k
    bool overflow = (((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0);
554
795k
    SetFlag(FLAG_PV, overflow);
555
795k
    A = (uint8_t)result;
556
795k
}
557
558
// sbc8 subtracts an 8-bit value and carry from the accumulator and updates flags
559
void Z80::sbc8(uint8_t value)
560
252
{
561
252
    uint8_t a = A;
562
252
    bool carry = GetFlag(FLAG_C);
563
252
    uint16_t result;
564
252
    if (carry)
565
0
    {
566
0
        result = (uint16_t)a - (uint16_t)value - 1;
567
0
    }
568
252
    else
569
252
    {
570
252
        result = (uint16_t)a - (uint16_t)value;
571
252
    }
572
252
    SetFlag(FLAG_C, (int)a - (int)value - (int)(carry ? 1 : 0) < 0);
573
252
    SetFlag(FLAG_H, (a & 0x0F) < (value & 0x0F) + (carry ? 1 : 0));
574
252
    SetFlag(FLAG_N, true);
575
252
    UpdateSZXYFlags((uint8_t)result);
576
    // Set overflow flag: overflow occurs if subtracting two numbers with different signs
577
    // produces a result with the same sign as the subtrahend
578
252
    uint8_t originalValue = a;
579
252
    if (carry)
580
0
    {
581
0
        bool overflow = (((originalValue ^ value) & 0x80) != 0) && (((originalValue ^ result) & 0x80) != 0);
582
0
        SetFlag(FLAG_PV, overflow);
583
0
    }
584
252
    else
585
252
    {
586
252
        bool overflow = (((originalValue ^ value) & 0x80) != 0) && (((originalValue ^ result) & 0x80) != 0);
587
252
        SetFlag(FLAG_PV, overflow);
588
252
    }
589
252
    A = (uint8_t)result;
590
252
}
591
592
// and8 performs bitwise AND with the accumulator and updates flags
593
void Z80::and8(uint8_t value)
594
11.8M
{
595
11.8M
    A &= value;
596
11.8M
    SetFlag(FLAG_C, false);
597
11.8M
    SetFlag(FLAG_N, false);
598
11.8M
    SetFlag(FLAG_H, true);
599
11.8M
    UpdateSZXYFlags(A);
600
    // For logical operations, P/V flag indicates parity
601
11.8M
    SetFlag(FLAG_PV, parity(A));
602
11.8M
}
603
604
// xor8 performs bitwise XOR with the accumulator and updates flags
605
void Z80::xor8(uint8_t value)
606
12.4M
{
607
12.4M
    A ^= value;
608
12.4M
    SetFlag(FLAG_C, false);
609
12.4M
    SetFlag(FLAG_N, false);
610
12.4M
    SetFlag(FLAG_H, false);
611
12.4M
    UpdateSZXYFlags(A);
612
    // For logical operations, P/V flag indicates parity
613
12.4M
    SetFlag(FLAG_PV, parity(A));
614
12.4M
}
615
616
// or8 performs bitwise OR with the accumulator and updates flags
617
void Z80::or8(uint8_t value)
618
1.66M
{
619
1.66M
    A |= value;
620
1.66M
    SetFlag(FLAG_C, false);
621
1.66M
    SetFlag(FLAG_N, false);
622
1.66M
    SetFlag(FLAG_H, false);
623
1.66M
    UpdateSZXYFlags(A);
624
    // For logical operations, P/V flag indicates parity
625
1.66M
    SetFlag(FLAG_PV, parity(A));
626
1.66M
}
627
628
// cp8 compares an 8-bit value with the accumulator and updates flags
629
void Z80::cp8(uint8_t value)
630
1.56M
{
631
1.56M
    uint8_t a = A;
632
1.56M
    uint16_t result = (uint16_t)a - (uint16_t)value;
633
1.56M
    SetFlag(FLAG_C, a < value);
634
1.56M
    SetFlag(FLAG_H, (a & 0x0F) < (value & 0x0F));
635
1.56M
    SetFlag(FLAG_N, true);
636
1.56M
    UpdateSZFlags((uint8_t)result);
637
    // For CP instruction, X and Y flags are set from the operand, not the result
638
1.56M
    UpdateFlags3and5FromValue(value);
639
    // Set overflow flag: overflow occurs if subtracting two numbers with different signs
640
    // produces a result with the same sign as the subtrahend
641
1.56M
    bool overflow = (((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0);
642
1.56M
    SetFlag(FLAG_PV, overflow);
643
1.56M
}
/Users/kiltum/projects/zxcpp/src/z80_cb_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
4
// Implementation of CB prefixed Z80 opcodes (bit manipulation instructions)
5
int Z80::ExecuteCBOpcode()
6
1.70M
{
7
    // Read the opcode from memory at the current program counter
8
1.70M
    uint8_t opcode = ReadOpcode();
9
    // R should not be incremented twice (already incremented in ExecuteOneInstruction for CB prefix)
10
    /// R = (R & 0x80) | ((R - 1) & 0x7F);
11
1.70M
    R++;
12
13
    // Handle rotate and shift instructions (0x00-0x3F)
14
1.70M
    if (opcode <= 0x3F)
15
816k
    {
16
        // Determine operation type from opcode bits 3-5
17
816k
        uint8_t opType = (opcode >> 3) & 0x07;
18
        // Determine register from opcode bits 0-2
19
816k
        uint8_t reg = opcode & 0x07;
20
21
        // Handle (HL) special case
22
816k
        if (reg == 6)
23
4.47k
        {
24
4.47k
            uint16_t addr = HL;
25
4.47k
            uint8_t value = memory->ReadByte(addr);
26
27
4.47k
            switch (opType)
28
4.47k
            {
29
4.47k
            case 0: // RLC
30
4.47k
            {
31
4.47k
                uint8_t result = rlc(value);
32
4.47k
                memory->WriteByte(addr, result);
33
4.47k
            }
34
4.47k
                return 15;
35
0
            case 1: // RRC
36
0
            {
37
0
                uint8_t result = rrc(value);
38
0
                memory->WriteByte(addr, result);
39
0
            }
40
0
                return 15;
41
0
            case 2: // RL
42
0
            {
43
0
                uint8_t result = rl(value);
44
0
                memory->WriteByte(addr, result);
45
0
            }
46
0
                return 15;
47
0
            case 3: // RR
48
0
            {
49
0
                uint8_t result = rr(value);
50
0
                memory->WriteByte(addr, result);
51
0
            }
52
0
                return 15;
53
0
            case 4: // SLA
54
0
            {
55
0
                uint8_t result = sla(value);
56
0
                memory->WriteByte(addr, result);
57
0
            }
58
0
                return 15;
59
0
            case 5: // SRA
60
0
            {
61
0
                uint8_t result = sra(value);
62
0
                memory->WriteByte(addr, result);
63
0
            }
64
0
                return 15;
65
0
            case 6: // SLL (Undocumented)
66
0
            {
67
0
                uint8_t result = sll(value);
68
0
                memory->WriteByte(addr, result);
69
0
            }
70
0
                return 15;
71
0
            case 7: // SRL
72
0
            {
73
0
                uint8_t result = srl(value);
74
0
                memory->WriteByte(addr, result);
75
0
            }
76
0
                return 15;
77
4.47k
            }
78
4.47k
        }
79
811k
        else
80
811k
        {
81
            // Handle regular registers
82
811k
            switch (opType)
83
811k
            {
84
1.34k
            case 0: // RLC
85
1.34k
                switch (reg)
86
1.34k
                {
87
520
                case 0:
88
520
                    B = rlc(B);
89
520
                    break;
90
0
                case 1:
91
0
                    C = rlc(C);
92
0
                    break;
93
825
                case 2:
94
825
                    D = rlc(D);
95
825
                    break;
96
0
                case 3:
97
0
                    E = rlc(E);
98
0
                    break;
99
0
                case 4:
100
0
                    H = rlc(H);
101
0
                    break;
102
0
                case 5:
103
0
                    L = rlc(L);
104
0
                    break;
105
0
                case 7:
106
0
                    A = rlc(A);
107
0
                    break;
108
1.34k
                }
109
1.34k
                return 8;
110
0
            case 1: // RRC
111
0
                switch (reg)
112
0
                {
113
0
                case 0:
114
0
                    B = rrc(B);
115
0
                    break;
116
0
                case 1:
117
0
                    C = rrc(C);
118
0
                    break;
119
0
                case 2:
120
0
                    D = rrc(D);
121
0
                    break;
122
0
                case 3:
123
0
                    E = rrc(E);
124
0
                    break;
125
0
                case 4:
126
0
                    H = rrc(H);
127
0
                    break;
128
0
                case 5:
129
0
                    L = rrc(L);
130
0
                    break;
131
0
                case 7:
132
0
                    A = rrc(A);
133
0
                    break;
134
0
                }
135
0
                return 8;
136
583k
            case 2: // RL
137
583k
                switch (reg)
138
583k
                {
139
48.3k
                case 0:
140
48.3k
                    B = rl(B);
141
48.3k
                    break;
142
50.3k
                case 1:
143
50.3k
                    C = rl(C);
144
50.3k
                    break;
145
48.3k
                case 2:
146
48.3k
                    D = rl(D);
147
48.3k
                    break;
148
95.7k
                case 3:
149
95.7k
                    E = rl(E);
150
95.7k
                    break;
151
0
                case 4:
152
0
                    H = rl(H);
153
0
                    break;
154
340k
                case 5:
155
340k
                    L = rl(L);
156
340k
                    break;
157
0
                case 7:
158
0
                    A = rl(A);
159
0
                    break;
160
583k
                }
161
583k
                return 8;
162
77.4k
            case 3: // RR
163
77.4k
                switch (reg)
164
77.4k
                {
165
16.2k
                case 0:
166
16.2k
                    B = rr(B);
167
16.2k
                    break;
168
16.2k
                case 1:
169
16.2k
                    C = rr(C);
170
16.2k
                    break;
171
28.7k
                case 2:
172
28.7k
                    D = rr(D);
173
28.7k
                    break;
174
16.2k
                case 3:
175
16.2k
                    E = rr(E);
176
16.2k
                    break;
177
0
                case 4:
178
0
                    H = rr(H);
179
0
                    break;
180
10
                case 5:
181
10
                    L = rr(L);
182
10
                    break;
183
0
                case 7:
184
0
                    A = rr(A);
185
0
                    break;
186
77.4k
                }
187
77.4k
                return 8;
188
59.3k
            case 4: // SLA
189
59.3k
                switch (reg)
190
59.3k
                {
191
6.91k
                case 0:
192
6.91k
                    B = sla(B);
193
6.91k
                    break;
194
864
                case 1:
195
864
                    C = sla(C);
196
864
                    break;
197
47.3k
                case 2:
198
47.3k
                    D = sla(D);
199
47.3k
                    break;
200
4.13k
                case 3:
201
4.13k
                    E = sla(E);
202
4.13k
                    break;
203
0
                case 4:
204
0
                    H = sla(H);
205
0
                    break;
206
45
                case 5:
207
45
                    L = sla(L);
208
45
                    break;
209
12
                case 7:
210
12
                    A = sla(A);
211
12
                    break;
212
59.3k
                }
213
59.3k
                return 8;
214
68.0k
            case 5: // SRA
215
68.0k
                switch (reg)
216
68.0k
                {
217
0
                case 0:
218
0
                    B = sra(B);
219
0
                    break;
220
0
                case 1:
221
0
                    C = sra(C);
222
0
                    break;
223
0
                case 2:
224
0
                    D = sra(D);
225
0
                    break;
226
0
                case 3:
227
0
                    E = sra(E);
228
0
                    break;
229
0
                case 4:
230
0
                    H = sra(H);
231
0
                    break;
232
0
                case 5:
233
0
                    L = sra(L);
234
0
                    break;
235
68.0k
                case 7:
236
68.0k
                    A = sra(A);
237
68.0k
                    break;
238
68.0k
                }
239
68.0k
                return 8;
240
0
            case 6: // SLL (Undocumented)
241
0
                switch (reg)
242
0
                {
243
0
                case 0:
244
0
                    B = sll(B);
245
0
                    break;
246
0
                case 1:
247
0
                    C = sll(C);
248
0
                    break;
249
0
                case 2:
250
0
                    D = sll(D);
251
0
                    break;
252
0
                case 3:
253
0
                    E = sll(E);
254
0
                    break;
255
0
                case 4:
256
0
                    H = sll(H);
257
0
                    break;
258
0
                case 5:
259
0
                    L = sll(L);
260
0
                    break;
261
0
                case 7:
262
0
                    A = sll(A);
263
0
                    break;
264
0
                }
265
0
                return 8;
266
22.1k
            case 7: // SRL
267
22.1k
                switch (reg)
268
22.1k
                {
269
0
                case 0:
270
0
                    B = srl(B);
271
0
                    break;
272
0
                case 1:
273
0
                    C = srl(C);
274
0
                    break;
275
9.60k
                case 2:
276
9.60k
                    D = srl(D);
277
9.60k
                    break;
278
12.4k
                case 3:
279
12.4k
                    E = srl(E);
280
12.4k
                    break;
281
102
                case 4:
282
102
                    H = srl(H);
283
102
                    break;
284
2
                case 5:
285
2
                    L = srl(L);
286
2
                    break;
287
0
                case 7:
288
0
                    A = srl(A);
289
0
                    break;
290
22.1k
                }
291
22.1k
                return 8;
292
811k
            }
293
811k
        }
294
816k
    }
295
296
    // Handle bit test instructions (0x40-0x7F)
297
891k
    if (opcode >= 0x40 && opcode <= 0x7F)
298
891k
    {
299
891k
        uint8_t bitNum = (opcode >> 3) & 0x07;
300
891k
        uint8_t reg = opcode & 0x07;
301
302
        // Handle (HL) special case
303
891k
        if (reg == 6)
304
136k
        {
305
136k
            uint8_t value = memory->ReadByte(HL);
306
136k
            bitMem(bitNum, value, uint8_t(MEMPTR >> 8));
307
136k
            return 12;
308
136k
        }
309
755k
        else
310
755k
        {
311
            // Handle regular registers
312
755k
            uint8_t regValue;
313
755k
            switch (reg)
314
755k
            {
315
0
            case 0:
316
0
                regValue = B;
317
0
                break;
318
148k
            case 1:
319
148k
                regValue = C;
320
148k
                break;
321
0
            case 2:
322
0
                regValue = D;
323
0
                break;
324
606k
            case 3:
325
606k
                regValue = E;
326
606k
                break;
327
1
            case 4:
328
1
                regValue = H;
329
1
                break;
330
351
            case 5:
331
351
                regValue = L;
332
351
                break;
333
5
            case 7:
334
5
                regValue = A;
335
5
                break;
336
755k
            }
337
755k
            bit(bitNum, regValue);
338
755k
            return 8;
339
755k
        }
340
891k
    }
341
342
    // Handle reset bit instructions (0x80-0xBF)
343
305
    if (opcode >= 0x80 && opcode <= 0xBF)
344
225
    {
345
225
        uint8_t bitNum = (opcode >> 3) & 0x07;
346
225
        uint8_t reg = opcode & 0x07;
347
348
        // Handle (HL) special case
349
225
        if (reg == 6)
350
225
        {
351
225
            uint16_t addr = HL;
352
225
            uint8_t value = memory->ReadByte(addr);
353
225
            uint8_t result = res(bitNum, value);
354
225
            memory->WriteByte(addr, result);
355
225
            return 15;
356
225
        }
357
0
        else
358
0
        {
359
            // Handle regular registers
360
0
            switch (reg)
361
0
            {
362
0
            case 0:
363
0
                B = res(bitNum, B);
364
0
                break;
365
0
            case 1:
366
0
                C = res(bitNum, C);
367
0
                break;
368
0
            case 2:
369
0
                D = res(bitNum, D);
370
0
                break;
371
0
            case 3:
372
0
                E = res(bitNum, E);
373
0
                break;
374
0
            case 4:
375
0
                H = res(bitNum, H);
376
0
                break;
377
0
            case 5:
378
0
                L = res(bitNum, L);
379
0
                break;
380
0
            case 7:
381
0
                A = res(bitNum, A);
382
0
                break;
383
0
            }
384
0
            return 8;
385
0
        }
386
225
    }
387
388
    // Handle set bit instructions (0xC0-0xFF)
389
80
    if (opcode >= 0xC0)
390
80
    {
391
80
        uint8_t bitNum = (opcode >> 3) & 0x07;
392
80
        uint8_t reg = opcode & 0x07;
393
394
        // Handle (HL) special case
395
80
        if (reg == 6)
396
80
        {
397
80
            uint16_t addr = HL;
398
80
            uint8_t value = memory->ReadByte(addr);
399
80
            uint8_t result = set(bitNum, value);
400
80
            memory->WriteByte(addr, result);
401
80
            return 15;
402
80
        }
403
0
        else
404
0
        {
405
            // Handle regular registers
406
0
            switch (reg)
407
0
            {
408
0
            case 0:
409
0
                B = set(bitNum, B);
410
0
                break;
411
0
            case 1:
412
0
                C = set(bitNum, C);
413
0
                break;
414
0
            case 2:
415
0
                D = set(bitNum, D);
416
0
                break;
417
0
            case 3:
418
0
                E = set(bitNum, E);
419
0
                break;
420
0
            case 4:
421
0
                H = set(bitNum, H);
422
0
                break;
423
0
            case 5:
424
0
                L = set(bitNum, L);
425
0
                break;
426
0
            case 7:
427
0
                A = set(bitNum, A);
428
0
                break;
429
0
            }
430
0
            return 8;
431
0
        }
432
80
    }
433
434
    // Unimplemented opcode
435
0
    return 8;
436
80
}
437
438
// rlc rotates a byte left circular
439
uint8_t Z80::rlc(uint8_t value)
440
5.81k
{
441
5.81k
    uint8_t result = (value << 1) | (value >> 7);
442
5.81k
    SetFlag(FLAG_C, (value & 0x80) != 0);
443
5.81k
    ClearFlag(FLAG_H);
444
5.81k
    ClearFlag(FLAG_N);
445
5.81k
    UpdateSZXYPVFlags(result);
446
5.81k
    return result;
447
5.81k
}
448
449
// rrc rotates a byte right circular
450
uint8_t Z80::rrc(uint8_t value)
451
0
{
452
0
    uint8_t result = (value >> 1) | (value << 7);
453
0
    SetFlag(FLAG_C, (value & 0x01) != 0);
454
0
    ClearFlag(FLAG_H);
455
0
    ClearFlag(FLAG_N);
456
0
    UpdateSZXYPVFlags(result);
457
0
    return result;
458
0
}
459
460
// rl rotates a byte left through carry
461
uint8_t Z80::rl(uint8_t value)
462
583k
{
463
583k
    bool oldCarry = GetFlag(FLAG_C);
464
583k
    uint8_t result = (value << 1);
465
583k
    if (oldCarry)
466
211k
    {
467
211k
        result |= 0x01;
468
211k
    }
469
583k
    SetFlag(FLAG_C, (value & 0x80) != 0);
470
583k
    ClearFlag(FLAG_H);
471
583k
    ClearFlag(FLAG_N);
472
583k
    UpdateSZXYPVFlags(result);
473
583k
    return result;
474
583k
}
475
476
// rr rotates a byte right through carry
477
uint8_t Z80::rr(uint8_t value)
478
77.4k
{
479
77.4k
    bool oldCarry = GetFlag(FLAG_C);
480
77.4k
    uint8_t result = (value >> 1);
481
77.4k
    if (oldCarry)
482
26.8k
    {
483
26.8k
        result |= 0x80;
484
26.8k
    }
485
77.4k
    SetFlag(FLAG_C, (value & 0x01) != 0);
486
77.4k
    ClearFlag(FLAG_H);
487
77.4k
    ClearFlag(FLAG_N);
488
77.4k
    UpdateSZXYPVFlags(result);
489
77.4k
    return result;
490
77.4k
}
491
492
// sla shifts a byte left arithmetic
493
uint8_t Z80::sla(uint8_t value)
494
59.3k
{
495
59.3k
    uint8_t result = value << 1;
496
59.3k
    SetFlag(FLAG_C, (value & 0x80) != 0);
497
59.3k
    ClearFlag(FLAG_H);
498
59.3k
    ClearFlag(FLAG_N);
499
59.3k
    UpdateSZXYPVFlags(result);
500
59.3k
    return result;
501
59.3k
}
502
503
// sra shifts a byte right arithmetic
504
uint8_t Z80::sra(uint8_t value)
505
68.0k
{
506
68.0k
    uint8_t result = (value >> 1) | (value & 0x80);
507
68.0k
    SetFlag(FLAG_C, (value & 0x01) != 0);
508
68.0k
    ClearFlag(FLAG_H);
509
68.0k
    ClearFlag(FLAG_N);
510
68.0k
    UpdateSZXYPVFlags(result);
511
68.0k
    return result;
512
68.0k
}
513
514
// sll shifts a byte left logical (Undocumented)
515
uint8_t Z80::sll(uint8_t value)
516
0
{
517
0
    uint8_t result = (value << 1) | 0x01;
518
0
    SetFlag(FLAG_C, (value & 0x80) != 0);
519
0
    ClearFlag(FLAG_H);
520
0
    ClearFlag(FLAG_N);
521
0
    UpdateSZXYPVFlags(result);
522
0
    return result;
523
0
}
524
525
// srl shifts a byte right logical
526
uint8_t Z80::srl(uint8_t value)
527
22.1k
{
528
22.1k
    uint8_t result = value >> 1;
529
22.1k
    SetFlag(FLAG_C, (value & 0x01) != 0);
530
22.1k
    ClearFlag(FLAG_H);
531
22.1k
    ClearFlag(FLAG_N);
532
22.1k
    UpdateSZXYPVFlags(result);
533
22.1k
    return result;
534
22.1k
}
535
536
// bit tests a bit in a byte
537
void Z80::bit(uint8_t bitNum, uint8_t value)
538
755k
{
539
755k
    uint8_t mask = uint8_t(1 << bitNum);
540
755k
    uint8_t result = value & mask;
541
755k
    SetFlag(FLAG_Z, result == 0);
542
755k
    SetFlag(FLAG_Y, (value & (1 << 5)) != 0);
543
755k
    SetFlag(FLAG_X, (value & (1 << 3)) != 0);
544
755k
    SetFlag(FLAG_H, true);
545
755k
    ClearFlag(FLAG_N);
546
755k
    if (result == 0)
547
663k
    {
548
663k
        SetFlag(FLAG_PV, true);
549
663k
        ClearFlag(FLAG_S);
550
663k
    }
551
91.9k
    else
552
91.9k
    {
553
91.9k
        ClearFlag(FLAG_PV);
554
        // For BIT 7, S flag is set to the value of bit 7
555
91.9k
        if (bitNum == 7)
556
91.6k
        {
557
91.6k
            SetFlag(FLAG_S, (value & 0x80) != 0);
558
91.6k
        }
559
321
        else
560
321
        {
561
321
            ClearFlag(FLAG_S);
562
321
        }
563
91.9k
    }
564
755k
}
565
566
// bitMem tests a bit in a byte for memory references
567
void Z80::bitMem(uint8_t bitNum, uint8_t value, uint8_t addrHi)
568
138k
{
569
138k
    uint8_t mask = uint8_t(1 << bitNum);
570
138k
    uint8_t result = value & mask;
571
138k
    SetFlag(FLAG_Z, result == 0);
572
138k
    SetFlag(FLAG_Y, (addrHi & (1 << 5)) != 0);
573
138k
    SetFlag(FLAG_X, (addrHi & (1 << 3)) != 0);
574
138k
    SetFlag(FLAG_H, true);
575
138k
    ClearFlag(FLAG_N);
576
138k
    if (result == 0)
577
136k
    {
578
136k
        SetFlag(FLAG_PV, true);
579
136k
        ClearFlag(FLAG_S);
580
136k
    }
581
2.01k
    else
582
2.01k
    {
583
2.01k
        ClearFlag(FLAG_PV);
584
        // For BIT 7, S flag is set to the value of bit 7
585
2.01k
        if (bitNum == 7)
586
1.78k
        {
587
1.78k
            SetFlag(FLAG_S, (value & 0x80) != 0);
588
1.78k
        }
589
224
        else
590
224
        {
591
224
            ClearFlag(FLAG_S);
592
224
        }
593
2.01k
    }
594
138k
}
595
596
// res resets a bit in a byte
597
uint8_t Z80::res(uint8_t bitNum, uint8_t value)
598
261
{
599
261
    uint8_t mask = uint8_t(~(1 << bitNum));
600
261
    return value & mask;
601
261
}
602
603
// set sets a bit in a byte
604
uint8_t Z80::set(uint8_t bitNum, uint8_t value)
605
97
{
606
97
    uint8_t mask = uint8_t(1 << bitNum);
607
97
    return value | mask;
608
97
}
/Users/kiltum/projects/zxcpp/src/z80_dd_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
4
// Implementation of DD prefixed Z80 opcodes (IX instructions)
5
int Z80::ExecuteDDOpcode()
6
775k
{
7
    // Read the opcode from memory at the current program counter
8
775k
    uint8_t opcode = ReadOpcode();
9
    // R should not be incremented twice (already incremented in ExecuteOneInstruction for DD prefix)
10
    // R = (R & 0x80) | ((R - 1) & 0x7F);
11
775k
    R++;
12
775k
    switch (opcode)
13
775k
    {
14
    // Load instructions
15
23
    case 0x09: // ADD IX, BC
16
23
    {
17
23
        uint16_t oldIX = IX;
18
23
        uint16_t result = add16IX(IX, BC);
19
23
        MEMPTR = oldIX + 1;
20
23
        IX = result;
21
23
    }
22
23
        return 15;
23
87.2k
    case 0x19: // ADD IX, DE
24
87.2k
    {
25
87.2k
        uint16_t oldIX = IX;
26
87.2k
        uint16_t result = add16IX(IX, DE);
27
87.2k
        MEMPTR = oldIX + 1;
28
87.2k
        IX = result;
29
87.2k
    }
30
87.2k
        return 15;
31
19.9k
    case 0x21: // LD IX, nn
32
19.9k
        IX = ReadImmediateWord();
33
19.9k
        return 14;
34
2
    case 0x22: // LD (nn), IX
35
2
    {
36
2
        uint16_t addr = ReadImmediateWord();
37
2
        memory->WriteByte(addr, uint8_t(IX & 0xFF));
38
2
        memory->WriteByte(addr + 1, uint8_t((IX >> 8) & 0xFF));
39
2
        MEMPTR = addr + 1;
40
2
    }
41
2
        return 20;
42
54.1k
    case 0x23: // INC IX
43
54.1k
        IX++;
44
54.1k
        return 10;
45
0
    case 0x24: // INC IXH
46
0
        SetIXH(inc8(GetIXH()));
47
0
        return 8;
48
0
    case 0x25: // DEC IXH
49
0
        SetIXH(dec8(GetIXH()));
50
0
        return 8;
51
0
    case 0x26: // LD IXH, n
52
0
        SetIXH(ReadImmediateByte());
53
0
        return 11;
54
0
    case 0x29: // ADD IX, IX
55
0
    {
56
0
        uint16_t oldIX = IX;
57
0
        uint16_t result = add16IX(IX, IX);
58
0
        MEMPTR = oldIX + 1;
59
0
        IX = result;
60
0
    }
61
0
        return 15;
62
3
    case 0x2A: // LD IX, (nn)
63
3
    {
64
3
        uint16_t addr = ReadImmediateWord();
65
3
        IX = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
66
3
        MEMPTR = addr + 1;
67
3
    }
68
3
        return 20;
69
4.06k
    case 0x2B: // DEC IX
70
4.06k
        IX--;
71
4.06k
        return 10;
72
0
    case 0x2C: // INC IXL
73
0
        SetIXL(inc8(GetIXL()));
74
0
        return 8;
75
0
    case 0x2D: // DEC IXL
76
0
        SetIXL(dec8(GetIXL()));
77
0
        return 8;
78
0
    case 0x2E: // LD IXL, n
79
0
        SetIXL(ReadImmediateByte());
80
0
        return 11;
81
8
    case 0x34: // INC (IX+d)
82
8
        return executeIncDecIndexed(true);
83
12.2k
    case 0x35: // DEC (IX+d)
84
12.2k
        return executeIncDecIndexed(false);
85
2.86k
    case 0x36: // LD (IX+d), n
86
2.86k
    {
87
2.86k
        int8_t displacement = ReadDisplacement();
88
2.86k
        uint8_t value = ReadImmediateByte();
89
2.86k
        uint16_t addr = uint16_t(int32_t(IX) + int32_t(displacement));
90
2.86k
        memory->WriteByte(addr, value);
91
2.86k
        MEMPTR = addr;
92
2.86k
    }
93
2.86k
        return 19;
94
0
    case 0x39: // ADD IX, SP
95
0
    {
96
0
        uint16_t oldIX = IX;
97
0
        uint16_t result = add16IX(IX, SP);
98
0
        MEMPTR = oldIX + 1;
99
0
        IX = result;
100
0
    }
101
0
        return 15;
102
0
    case 0x40: // LD B,B
103
0
        return 8;
104
105
    // Load register from IX register
106
0
    case 0x44: // LD B, IXH
107
0
        B = GetIXH();
108
0
        return 8;
109
0
    case 0x45: // LD B, IXL
110
0
        B = GetIXL();
111
0
        return 8;
112
6
    case 0x46: // LD B, (IX+d)
113
6
        return executeLoadFromIndexed(0);
114
0
    case 0x4C: // LD C, IXH
115
0
        C = GetIXH();
116
0
        return 8;
117
0
    case 0x4D: // LD C, IXL
118
0
        C = GetIXL();
119
0
        return 8;
120
4.35k
    case 0x4E: // LD C, (IX+d)
121
4.35k
        return executeLoadFromIndexed(1);
122
0
    case 0x54: // LD D, IXH
123
0
        D = GetIXH();
124
0
        return 8;
125
0
    case 0x55: // LD D, IXL
126
0
        D = GetIXL();
127
0
        return 8;
128
4.30k
    case 0x56: // LD D, (IX+d)
129
4.30k
        return executeLoadFromIndexed(2);
130
0
    case 0x5C: // LD E, IXH
131
0
        E = GetIXH();
132
0
        return 8;
133
0
    case 0x5D: // LD E, IXL
134
0
        E = GetIXL();
135
0
        return 8;
136
4.30k
    case 0x5E: // LD E, (IX+d)
137
4.30k
        return executeLoadFromIndexed(3);
138
0
    case 0x60: // LD IXH, B
139
0
        SetIXH(B);
140
0
        return 8;
141
0
    case 0x61: // LD IXH, C
142
0
        SetIXH(C);
143
0
        return 8;
144
0
    case 0x62: // LD IXH, D
145
0
        SetIXH(D);
146
0
        return 8;
147
0
    case 0x63: // LD IXH, E
148
0
        SetIXH(E);
149
0
        return 8;
150
0
    case 0x64: // LD IXH, IXH
151
        // No operation needed
152
0
        return 8;
153
0
    case 0x65: // LD IXH, IXL
154
0
        SetIXH(GetIXL());
155
0
        return 8;
156
4.33k
    case 0x66: // LD H, (IX+d)
157
4.33k
        return executeLoadFromIndexed(4);
158
0
    case 0x67: // LD IXH, A
159
0
        SetIXH(A);
160
0
        return 8;
161
0
    case 0x68: // LD IXL, B
162
0
        SetIXL(B);
163
0
        return 8;
164
0
    case 0x69: // LD IXL, C
165
0
        SetIXL(C);
166
0
        return 8;
167
0
    case 0x6A: // LD IXL, D
168
0
        SetIXL(D);
169
0
        return 8;
170
0
    case 0x6B: // LD IXL, E
171
0
        SetIXL(E);
172
0
        return 8;
173
0
    case 0x6C: // LD IXL, IXH
174
0
        SetIXL(GetIXH());
175
0
        return 8;
176
0
    case 0x6D: // LD IXL, IXL
177
        // No operation needed
178
0
        return 8;
179
4.33k
    case 0x6E: // LD L, (IX+d)
180
4.33k
        return executeLoadFromIndexed(5);
181
0
    case 0x6F: // LD IXL, A
182
0
        SetIXL(A);
183
0
        return 8;
184
236
    case 0x70: // LD (IX+d), B
185
236
        return executeStoreToIndexed(B);
186
568
    case 0x71: // LD (IX+d), C
187
568
        return executeStoreToIndexed(C);
188
3.45k
    case 0x72: // LD (IX+d), D
189
3.45k
        return executeStoreToIndexed(D);
190
3.45k
    case 0x73: // LD (IX+d), E
191
3.45k
        return executeStoreToIndexed(E);
192
3.86k
    case 0x74: // LD (IX+d), H
193
3.86k
        return executeStoreToIndexed(H);
194
46.2k
    case 0x75: // LD (IX+d), L
195
46.2k
        return executeStoreToIndexed(L);
196
19.4k
    case 0x77: // LD (IX+d), A
197
19.4k
        return executeStoreToIndexed(A);
198
0
    case 0x7C: // LD A, IXH
199
0
        A = GetIXH();
200
0
        return 8;
201
0
    case 0x7D: // LD A, IXL
202
0
        A = GetIXL();
203
0
        return 8;
204
90.0k
    case 0x7E: // LD A, (IX+d)
205
90.0k
        return executeLoadFromIndexed(7);
206
207
    // Arithmetic and logic instructions
208
0
    case 0x84: // ADD A, IXH
209
0
        add8(GetIXH());
210
0
        return 8;
211
0
    case 0x85: // ADD A, IXL
212
0
        add8(GetIXL());
213
0
        return 8;
214
1.05k
    case 0x86: // ADD A, (IX+d)
215
1.05k
        return executeALUIndexed(0);
216
0
    case 0x8C: // ADC A, IXH
217
0
        adc8(GetIXH());
218
0
        return 8;
219
0
    case 0x8D: // ADC A, IXL
220
0
        adc8(GetIXL());
221
0
        return 8;
222
0
    case 0x8E: // ADC A, (IX+d)
223
0
        return executeALUIndexed(1);
224
0
    case 0x94: // SUB IXH
225
0
        sub8(GetIXH());
226
0
        return 8;
227
0
    case 0x95: // SUB IXL
228
0
        sub8(GetIXL());
229
0
        return 8;
230
17
    case 0x96: // SUB (IX+d)
231
17
        return executeALUIndexed(2);
232
0
    case 0x9C: // SBC A, IXH
233
0
        sbc8(GetIXH());
234
0
        return 8;
235
0
    case 0x9D: // SBC A, IXL
236
0
        sbc8(GetIXL());
237
0
        return 8;
238
0
    case 0x9E: // SBC A, (IX+d)
239
0
        return executeALUIndexed(3);
240
0
    case 0xA4: // AND IXH
241
0
        and8(GetIXH());
242
0
        return 8;
243
0
    case 0xA5: // AND IXL
244
0
        and8(GetIXL());
245
0
        return 8;
246
0
    case 0xA6: // AND (IX+d)
247
0
        return executeALUIndexed(4);
248
0
    case 0xAC: // XOR IXH
249
0
        xor8(GetIXH());
250
0
        return 8;
251
0
    case 0xAD: // XOR IXL
252
0
        xor8(GetIXL());
253
0
        return 8;
254
1
    case 0xAE: // XOR (IX+d)
255
1
        return executeALUIndexed(5);
256
0
    case 0xB4: // OR IXH
257
0
        or8(GetIXH());
258
0
        return 8;
259
0
    case 0xB5: // OR IXL
260
0
        or8(GetIXL());
261
0
        return 8;
262
4.29k
    case 0xB6: // OR (IX+d)
263
4.29k
        return executeALUIndexed(6);
264
0
    case 0xBC: // CP IXH
265
0
        cp8(GetIXH());
266
0
        return 8;
267
0
    case 0xBD: // CP IXL
268
0
        cp8(GetIXL());
269
0
        return 8;
270
618
    case 0xBE: // CP (IX+d)
271
618
        return executeALUIndexed(7);
272
273
    // POP and PUSH instructions
274
86.4k
    case 0xE1: // POP IX
275
86.4k
        IX = Pop();
276
86.4k
        return 14;
277
0
    case 0xE3: // EX (SP), IX
278
0
    {
279
0
        uint16_t temp = (uint16_t(memory->ReadByte(SP + 1)) << 8) | uint16_t(memory->ReadByte(SP));
280
0
        memory->WriteByte(SP, uint8_t(IX & 0xFF));
281
0
        memory->WriteByte(SP + 1, uint8_t((IX >> 8) & 0xFF));
282
0
        IX = temp;
283
0
        MEMPTR = temp;
284
0
    }
285
0
        return 23;
286
86.2k
    case 0xE5: // PUSH IX
287
86.2k
        Push(IX);
288
86.2k
        return 15;
289
227k
    case 0xE9: // JP (IX)
290
227k
        PC = IX;
291
227k
        return 8;
292
0
    case 0xF9: // LD SP, IX
293
0
        SP = IX;
294
0
        return 10;
295
296
    // Handle DD CB prefix (IX with displacement and CB operations)
297
0
    case 0xCB: // DD CB prefix
298
0
        return executeDDCBOpcode();
299
300
0
    case 0xfd:
301
0
        return 8;
302
0
    case 0x00: // Extended NOP (undocumented)
303
        // DD 00 is an undocumented instruction that acts as an extended NOP
304
        // It consumes the DD prefix and the 00 opcode but executes as a NOP
305
        // Takes 8 cycles total (4 for DD prefix fetch + 4 for 00 opcode fetch)
306
0
        return 8;
307
0
    case 0xdd:
308
0
        return 8;
309
0
    default:
310
0
        PC--;
311
0
        return ExecuteOpcode();
312
        // panic(fmt.Sprintf("DD unexpected code %x", opcode))
313
775k
    }
314
775k
}
315
316
// add16IX adds two 16-bit values for IX register and updates flags
317
uint16_t Z80::add16IX(uint16_t a, uint16_t b)
318
87.2k
{
319
87.2k
    uint32_t result = (uint32_t)a + (uint32_t)b;
320
87.2k
    SetFlag(FLAG_C, result > 0xFFFF);
321
87.2k
    SetFlag(FLAG_H, (a & 0x0FFF) + (b & 0x0FFF) > 0x0FFF);
322
87.2k
    ClearFlag(FLAG_N);
323
    // For IX operations, we update X and Y flags from high byte of result
324
87.2k
    UpdateFlags3and5FromAddress((uint16_t)result);
325
87.2k
    return (uint16_t)result;
326
87.2k
}
327
328
// Get IXH (high byte of IX)
329
uint8_t Z80::GetIXH()
330
0
{
331
0
    return uint8_t(IX >> 8);
332
0
}
333
334
// Get IXL (low byte of IX)
335
uint8_t Z80::GetIXL()
336
0
{
337
0
    return uint8_t(IX & 0xFF);
338
0
}
339
340
// Set IXH (high byte of IX)
341
void Z80::SetIXH(uint8_t value)
342
0
{
343
0
    IX = (IX & 0x00FF) | (uint16_t(value) << 8);
344
0
}
345
346
// Set IXL (low byte of IX)
347
void Z80::SetIXL(uint8_t value)
348
0
{
349
0
    IX = (IX & 0xFF00) | uint16_t(value);
350
0
}
351
352
// executeIncDecIndexed handles INC/DEC (IX+d) instructions
353
int Z80::executeIncDecIndexed(bool isInc)
354
12.2k
{
355
12.2k
    int8_t displacement = ReadDisplacement();
356
12.2k
    uint16_t addr = uint16_t(int32_t(IX) + int32_t(displacement));
357
12.2k
    uint8_t value = memory->ReadByte(addr);
358
12.2k
    uint8_t result;
359
12.2k
    if (isInc)
360
8
    {
361
8
        result = inc8(value);
362
8
    }
363
12.2k
    else
364
12.2k
    {
365
12.2k
        result = dec8(value);
366
12.2k
    }
367
12.2k
    memory->WriteByte(addr, result);
368
12.2k
    MEMPTR = addr;
369
12.2k
    return 23;
370
12.2k
}
371
372
// executeLoadFromIndexed handles LD r, (IX+d) instructions
373
int Z80::executeLoadFromIndexed(uint8_t reg)
374
111k
{
375
111k
    int8_t displacement = ReadDisplacement();
376
111k
    uint16_t addr = uint16_t(int32_t(IX) + int32_t(displacement));
377
111k
    uint8_t value = memory->ReadByte(addr);
378
379
111k
    switch (reg)
380
111k
    {
381
6
    case 0:
382
6
        B = value;
383
6
        break;
384
4.35k
    case 1:
385
4.35k
        C = value;
386
4.35k
        break;
387
4.30k
    case 2:
388
4.30k
        D = value;
389
4.30k
        break;
390
4.30k
    case 3:
391
4.30k
        E = value;
392
4.30k
        break;
393
4.33k
    case 4:
394
4.33k
        H = value;
395
4.33k
        break;
396
4.33k
    case 5:
397
4.33k
        L = value;
398
4.33k
        break;
399
90.0k
    case 7:
400
90.0k
        A = value;
401
90.0k
        break;
402
111k
    }
403
404
111k
    MEMPTR = addr;
405
111k
    return 19;
406
111k
}
407
408
// executeStoreToIndexed handles LD (IX+d), r instructions
409
int Z80::executeStoreToIndexed(uint8_t value)
410
77.2k
{
411
77.2k
    int8_t displacement = ReadDisplacement();
412
77.2k
    uint16_t addr = uint16_t(int32_t(IX) + int32_t(displacement));
413
77.2k
    memory->WriteByte(addr, value);
414
77.2k
    MEMPTR = addr;
415
77.2k
    return 19;
416
77.2k
}
417
418
// executeALUIndexed handles ALU operations with (IX+d) operand
419
int Z80::executeALUIndexed(uint8_t opType)
420
5.98k
{
421
5.98k
    int8_t displacement = ReadDisplacement();
422
5.98k
    uint16_t addr = uint16_t(int32_t(IX) + int32_t(displacement));
423
5.98k
    uint8_t value = memory->ReadByte(addr);
424
425
5.98k
    switch (opType)
426
5.98k
    {
427
1.05k
    case 0: // ADD
428
1.05k
        add8(value);
429
1.05k
        break;
430
0
    case 1: // ADC
431
0
        adc8(value);
432
0
        break;
433
17
    case 2: // SUB
434
17
        sub8(value);
435
17
        break;
436
0
    case 3: // SBC
437
0
        sbc8(value);
438
0
        break;
439
0
    case 4: // AND
440
0
        and8(value);
441
0
        break;
442
1
    case 5: // XOR
443
1
        xor8(value);
444
1
        break;
445
4.29k
    case 6: // OR
446
4.29k
        or8(value);
447
4.29k
        break;
448
618
    case 7: // CP
449
618
        cp8(value);
450
618
        break;
451
5.98k
    }
452
453
5.98k
    MEMPTR = addr;
454
5.98k
    return 19;
455
5.98k
}
/Users/kiltum/projects/zxcpp/src/z80_ddcb_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
4
// Implementation of DD CB prefixed Z80 opcodes (IX with displacement and CB operations)
5
int Z80::executeDDCBOpcode()
6
0
{
7
    // For DD CB prefixed instructions, R should be incremented by 2 total
8
    // We've already incremented R once for the DD prefix and once for the CB prefix
9
    // So we need to adjust by -1 to get the correct total increment of 2
10
0
    uint8_t originalR = R;
11
12
0
    int8_t displacement = ReadDisplacement();
13
0
    uint8_t opcode = ReadOpcode();
14
15
    // Adjust R register - DD CB instructions should increment R by 2 total
16
    // We've already incremented twice (DD and CB), so we need to subtract 1
17
    // to get the correct total of 2 increments
18
0
    R = originalR;
19
20
0
    uint16_t addr = uint16_t(int32_t(IX) + int32_t(displacement));
21
0
    uint8_t value = memory->ReadByte(addr);
22
23
    // Handle rotate and shift instructions (0x00-0x3F)
24
0
    if (opcode <= 0x3F)
25
0
    {
26
        // Determine operation type from opcode bits 3-5
27
0
        uint8_t opType = (opcode >> 3) & 0x07;
28
        // Determine register from opcode bits 0-2
29
0
        uint8_t reg = opcode & 0x07;
30
31
        // Perform the operation
32
0
        uint8_t result;
33
0
        switch (opType)
34
0
        {
35
0
        case 0: // RLC
36
0
            result = rlc(value);
37
0
            break;
38
0
        case 1: // RRC
39
0
            result = rrc(value);
40
0
            break;
41
0
        case 2: // RL
42
0
            result = rl(value);
43
0
            break;
44
0
        case 3: // RR
45
0
            result = rr(value);
46
0
            break;
47
0
        case 4: // SLA
48
0
            result = sla(value);
49
0
            break;
50
0
        case 5: // SRA
51
0
            result = sra(value);
52
0
            break;
53
0
        case 6: // SLL (Undocumented)
54
0
            result = sll(value);
55
0
            break;
56
0
        case 7: // SRL
57
0
            result = srl(value);
58
0
            break;
59
0
        default:
60
0
            result = value;
61
0
            break;
62
0
        }
63
64
        // Store result in memory
65
0
        memory->WriteByte(addr, result);
66
67
        // Store result in register if needed (except for (HL) case)
68
0
        if (reg != 6)
69
0
        { // reg 6 is (HL) - no register store needed
70
0
            switch (reg)
71
0
            {
72
0
            case 0:
73
0
                B = result;
74
0
                break;
75
0
            case 1:
76
0
                C = result;
77
0
                break;
78
0
            case 2:
79
0
                D = result;
80
0
                break;
81
0
            case 3:
82
0
                E = result;
83
0
                break;
84
0
            case 4:
85
0
                H = result;
86
0
                break;
87
0
            case 5:
88
0
                L = result;
89
0
                break;
90
0
            case 7:
91
0
                A = result;
92
0
                break;
93
0
            }
94
0
        }
95
96
0
        MEMPTR = addr;
97
0
        return 23;
98
0
    }
99
100
    // Handle bit test instructions (0x40-0x7F)
101
0
    if (opcode >= 0x40 && opcode <= 0x7F)
102
0
    {
103
0
        uint8_t bitNum = (opcode >> 3) & 0x07;
104
0
        bitMem(bitNum, value, uint8_t(addr >> 8));
105
0
        MEMPTR = addr;
106
0
        return 20;
107
0
    }
108
109
    // Handle reset bit instructions (0x80-0xBF)
110
0
    if (opcode >= 0x80 && opcode <= 0xBF)
111
0
    {
112
0
        uint8_t bitNum = (opcode >> 3) & 0x07;
113
0
        uint8_t reg = opcode & 0x07;
114
115
0
        uint8_t result = res(bitNum, value);
116
0
        memory->WriteByte(addr, result);
117
118
        // Store result in register if needed (except for (HL) case)
119
0
        if (reg != 6)
120
0
        { // reg 6 is (HL) - no register store needed
121
0
            switch (reg)
122
0
            {
123
0
            case 0:
124
0
                B = result;
125
0
                break;
126
0
            case 1:
127
0
                C = result;
128
0
                break;
129
0
            case 2:
130
0
                D = result;
131
0
                break;
132
0
            case 3:
133
0
                E = result;
134
0
                break;
135
0
            case 4:
136
0
                H = result;
137
0
                break;
138
0
            case 5:
139
0
                L = result;
140
0
                break;
141
0
            case 7:
142
0
                A = result;
143
0
                break;
144
0
            }
145
0
        }
146
147
0
        MEMPTR = addr;
148
0
        return 23;
149
0
    }
150
151
    // Handle set bit instructions (0xC0-0xFF)
152
0
    if (opcode >= 0xC0)
153
0
    {
154
0
        uint8_t bitNum = (opcode >> 3) & 0x07;
155
0
        uint8_t reg = opcode & 0x07;
156
157
0
        uint8_t result = set(bitNum, value);
158
0
        memory->WriteByte(addr, result);
159
160
        // Store result in register if needed (except for (HL) case)
161
0
        if (reg != 6)
162
0
        { // reg 6 is (HL) - no register store needed
163
0
            switch (reg)
164
0
            {
165
0
            case 0:
166
0
                B = result;
167
0
                break;
168
0
            case 1:
169
0
                C = result;
170
0
                break;
171
0
            case 2:
172
0
                D = result;
173
0
                break;
174
0
            case 3:
175
0
                E = result;
176
0
                break;
177
0
            case 4:
178
0
                H = result;
179
0
                break;
180
0
            case 5:
181
0
                L = result;
182
0
                break;
183
0
            case 7:
184
0
                A = result;
185
0
                break;
186
0
            }
187
0
        }
188
189
0
        MEMPTR = addr;
190
0
        return 23;
191
0
    }
192
193
    // Unimplemented opcode
194
0
    return 23;
195
0
}
/Users/kiltum/projects/zxcpp/src/z80_ed_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
4
// Implementation of ED prefixed Z80 opcodes (extended instructions)
5
int Z80::ExecuteEDOpcode()
6
2.24M
{
7
    // Read the opcode from memory at the current program counter
8
2.24M
    uint8_t opcode = ReadOpcode();
9
    // R should not be incremented twice (already incremented in ExecuteOneInstruction for ED prefix)
10
    // R = (R & 0x80) | ((R - 1) & 0x7F);
11
2.24M
    R++;
12
2.24M
    switch (opcode)
13
2.24M
    {
14
    // Block transfer instructions
15
281k
    case 0xA0: // LDI
16
281k
        ldi();
17
281k
        return 16;
18
0
    case 0xA1: // CPI
19
0
        cpi();
20
0
        return 16;
21
0
    case 0xA2: // INI
22
0
        ini();
23
0
        return 16;
24
0
    case 0xA3: // OUTI
25
0
        outi();
26
0
        return 16;
27
0
    case 0xA8: // LDD
28
0
        ldd();
29
0
        return 16;
30
0
    case 0xA9: // CPD
31
0
        cpd();
32
0
        return 16;
33
0
    case 0xAA: // IND
34
0
        ind();
35
0
        return 16;
36
0
    case 0xAB: // OUTD
37
0
        outd();
38
0
        return 16;
39
997k
    case 0xB0: // LDIR
40
997k
        return ldir();
41
0
    case 0xB1: // CPIR
42
0
        return cpir();
43
0
    case 0xB2: // INIR
44
0
        return inir();
45
0
    case 0xB3: // OTIR
46
0
        return otir();
47
51.5k
    case 0xB8: // LDDR
48
51.5k
        return lddr();
49
0
    case 0xB9: // CPDR
50
0
        return cpdr();
51
0
    case 0xBA: // INDR
52
0
        return indr();
53
0
    case 0xBB: // OTDR
54
0
        return otdr();
55
56
    // 8-bit load instructions
57
0
    case 0x40: // IN B, (C)
58
0
        return executeIN(0);
59
0
    case 0x41: // OUT (C), B
60
0
        return executeOUT(0);
61
32
    case 0x42: // SBC HL, BC
62
32
    {
63
32
        uint16_t result = sbc16WithMEMPTR(HL, BC);
64
32
        HL = result;
65
32
    }
66
32
        return 15;
67
307
    case 0x43: // LD (nn), BC
68
307
    {
69
307
        uint16_t addr = ReadImmediateWord();
70
307
        memory->WriteByte(addr, uint8_t(BC & 0xFF));
71
307
        memory->WriteByte(addr + 1, uint8_t((BC >> 8) & 0xFF));
72
        // MEMPTR = addr + 1
73
307
        MEMPTR = addr + 1;
74
307
    }
75
307
        return 20;
76
670k
    case 0x44: // NEG (various undocumented versions)
77
670k
    case 0x4C:
78
670k
    case 0x54:
79
670k
    case 0x5C:
80
670k
    case 0x64:
81
670k
    case 0x6C:
82
670k
    case 0x74:
83
670k
    case 0x7C:
84
670k
        neg();
85
670k
        return 8;
86
0
    case 0x45: // RETN (various undocumented versions)
87
0
    case 0x55:
88
0
    case 0x5D:
89
0
    case 0x65:
90
0
    case 0x6D:
91
0
    case 0x75:
92
0
    case 0x7D:
93
0
        retn();
94
0
        return 14;
95
0
    case 0x46: // IM 0 (various undocumented versions)
96
0
    case 0x4E:
97
0
    case 0x66:
98
0
        IM = 0;
99
0
        return 8;
100
0
    case 0x47: // LD I, A
101
0
        I = A;
102
0
        return 9;
103
0
    case 0x48: // IN C, (C)
104
0
        return executeIN(1);
105
0
    case 0x49: // OUT (C), C
106
0
        return executeOUT(1);
107
0
    case 0x4A: // ADC HL, BC
108
0
    {
109
0
        uint16_t result = adc16WithMEMPTR(HL, BC);
110
0
        HL = result;
111
0
    }
112
0
        return 15;
113
14.2k
    case 0x4B: // LD BC, (nn)
114
14.2k
    {
115
14.2k
        uint16_t addr = ReadImmediateWord();
116
14.2k
        BC = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
117
        // MEMPTR = addr + 1
118
14.2k
        MEMPTR = addr + 1;
119
14.2k
    }
120
14.2k
        return 20;
121
0
    case 0x4D: // RETI
122
0
        reti();
123
0
        return 14;
124
0
    case 0x4F: // LD R, A
125
        // R register is only 7 bits, bit 7 remains unchanged
126
0
        R = (R & 0x80) | (A & 0x7F);
127
        // gs
128
0
        R = A; // fix zen80 tests
129
0
        return 9;
130
1.51k
    case 0x50: // IN D, (C)
131
1.51k
        return executeIN(2);
132
0
    case 0x51: // OUT (C), D
133
0
        return executeOUT(2);
134
15.8k
    case 0x52: // SBC HL, DE
135
15.8k
    {
136
15.8k
        uint16_t result = sbc16WithMEMPTR(HL, DE);
137
15.8k
        HL = result;
138
15.8k
    }
139
15.8k
        return 15;
140
11.9k
    case 0x53: // LD (nn), DE
141
11.9k
    {
142
11.9k
        uint16_t addr = ReadImmediateWord();
143
11.9k
        memory->WriteByte(addr, uint8_t(DE & 0xFF));
144
11.9k
        memory->WriteByte(addr + 1, uint8_t((DE >> 8) & 0xFF));
145
        // MEMPTR = addr + 1
146
11.9k
        MEMPTR = addr + 1;
147
11.9k
    }
148
11.9k
        return 20;
149
1
    case 0x56: // IM 1 (various undocumented versions)
150
1
    case 0x76:
151
1
        IM = 1;
152
1
        return 8;
153
0
    case 0x57: // LD A, I
154
0
        ldAI();
155
0
        return 9;
156
0
    case 0x58: // IN E, (C)
157
0
        return executeIN(3);
158
0
    case 0x59: // OUT (C), E
159
0
        return executeOUT(3);
160
0
    case 0x5A: // ADC HL, DE
161
0
    {
162
0
        uint16_t result = adc16WithMEMPTR(HL, DE);
163
0
        HL = result;
164
0
    }
165
0
        return 15;
166
28.3k
    case 0x5B: // LD DE, (nn)
167
28.3k
    {
168
28.3k
        uint16_t addr = ReadImmediateWord();
169
28.3k
        DE = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
170
        // MEMPTR = addr + 1
171
28.3k
        MEMPTR = addr + 1;
172
28.3k
    }
173
28.3k
        return 20;
174
0
    case 0x5E: // IM 2 (various undocumented versions)
175
0
    case 0x7E:
176
0
        IM = 2;
177
0
        return 8;
178
2.47k
    case 0x5F: // LD A, R
179
2.47k
        ldAR();
180
2.47k
        return 9;
181
32
    case 0x60: // IN H, (C)
182
32
        return executeIN(4);
183
23.6k
    case 0x61: // OUT (C), H
184
23.6k
        return executeOUT(4);
185
0
    case 0x62: // SBC HL, HL
186
0
    {
187
0
        uint16_t result = sbc16WithMEMPTR(HL, HL);
188
0
        HL = result;
189
0
    }
190
0
        return 15;
191
0
    case 0x63: // LD (nn), HL
192
0
    {
193
0
        uint16_t addr = ReadImmediateWord();
194
0
        memory->WriteByte(addr, uint8_t(HL & 0xFF));
195
0
        memory->WriteByte(addr + 1, uint8_t((HL >> 8) & 0xFF));
196
        // MEMPTR = addr + 1
197
0
        MEMPTR = addr + 1;
198
0
    }
199
0
        return 20;
200
0
    case 0x67: // RRD
201
0
        rrd();
202
0
        return 18;
203
0
    case 0x68: // IN L, (C)
204
0
        return executeIN(5);
205
32
    case 0x69: // OUT (C), L
206
32
        return executeOUT(5);
207
0
    case 0x6A: // ADC HL, HL
208
0
    {
209
0
        uint16_t result = adc16WithMEMPTR(HL, HL);
210
0
        HL = result;
211
0
    }
212
0
        return 15;
213
0
    case 0x6B: // LD HL, (nn)
214
0
    {
215
0
        uint16_t addr = ReadImmediateWord();
216
0
        HL = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
217
        // MEMPTR = addr + 1
218
0
        MEMPTR = addr + 1;
219
0
    }
220
0
        return 20;
221
0
    case 0x6F: // RLD
222
0
        rld();
223
0
        return 18;
224
0
    case 0x70: // IN (C) (Undocumented - input to dummy register)
225
0
    {
226
0
        uint16_t bc = BC; // Save BC before doing anything
227
0
        uint8_t value = inC();
228
0
        UpdateSZXYFlags(value);
229
0
        ClearFlag(FLAG_H);
230
0
        ClearFlag(FLAG_N);
231
        // Set PV flag based on parity of the result
232
0
        SetFlag(FLAG_PV, parity(value));
233
        // MEMPTR = BC + 1 (using the original BC value)
234
0
        MEMPTR = bc + 1;
235
0
    }
236
0
        return 12;
237
0
    case 0x71: // OUT (C), 0 (Undocumented)
238
0
    {
239
0
        outC(0);
240
        // MEMPTR = BC + 1
241
0
        MEMPTR = BC + 1;
242
0
    }
243
0
        return 12;
244
16
    case 0x72: // SBC HL, SP
245
16
    {
246
16
        uint16_t result = sbc16WithMEMPTR(HL, SP);
247
16
        HL = result;
248
16
    }
249
16
        return 15;
250
61.9k
    case 0x73: // LD (nn), SP
251
61.9k
    {
252
61.9k
        uint16_t addr = ReadImmediateWord();
253
61.9k
        memory->WriteByte(addr, uint8_t(SP & 0xFF));
254
61.9k
        memory->WriteByte(addr + 1, uint8_t((SP >> 8) & 0xFF));
255
        // MEMPTR = addr + 1
256
61.9k
        MEMPTR = addr + 1;
257
61.9k
    }
258
61.9k
        return 20;
259
584
    case 0x78: // IN A, (C)
260
584
        return executeIN(7);
261
25.1k
    case 0x79: // OUT (C), A
262
25.1k
        return executeOUT(7);
263
0
    case 0x7A: // ADC HL, SP
264
0
    {
265
0
        uint16_t result = adc16WithMEMPTR(HL, SP);
266
0
        HL = result;
267
0
    }
268
0
        return 15;
269
61.9k
    case 0x7B: // LD SP, (nn)
270
61.9k
    {
271
61.9k
        uint16_t addr = ReadImmediateWord();
272
61.9k
        SP = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
273
        // MEMPTR = addr + 1
274
61.9k
        MEMPTR = addr + 1;
275
61.9k
    }
276
61.9k
        return 20;
277
0
    case 0x80: // undefined NOP
278
0
        return 8;
279
0
    case 0x6e:
280
0
        return 8;
281
282
0
    default:
283
        // For unimplemented opcodes, we just return a default cycle count
284
0
        return 4;
285
2.24M
    }
286
2.24M
}
287
288
// sbc16 subtracts 16-bit value with carry from HL
289
uint16_t Z80::sbc16(uint16_t val1, uint16_t val2)
290
15.8k
{
291
15.8k
    uint32_t carry = 0;
292
15.8k
    if (GetFlag(FLAG_C))
293
2
    {
294
2
        carry = 1;
295
2
    }
296
15.8k
    int32_t result = (int32_t)val1 - (int32_t)val2 - (int32_t)carry;
297
15.8k
    bool halfCarry = ((int16_t)(val1 & 0x0FFF) - (int16_t)(val2 & 0x0FFF) - (int16_t)carry) < 0;
298
15.8k
    bool overflow = (((val1 ^ val2) & 0x8000) != 0) && (((val1 ^ (uint16_t)result) & 0x8000) != 0);
299
300
15.8k
    uint16_t res16 = (uint16_t)result;
301
302
15.8k
    SetFlag(FLAG_S, (res16 & 0x8000) != 0);
303
15.8k
    SetFlag(FLAG_Z, res16 == 0);
304
15.8k
    SetFlag(FLAG_H, halfCarry);
305
15.8k
    SetFlag(FLAG_PV, overflow);
306
15.8k
    SetFlag(FLAG_N, true);
307
15.8k
    SetFlag(FLAG_C, result < 0);
308
    // FIX: Set X and Y flags from high byte of result
309
15.8k
    SetFlag(FLAG_X, (uint8_t(res16 >> 8) & FLAG_X) != 0);
310
15.8k
    SetFlag(FLAG_Y, (uint8_t(res16 >> 8) & FLAG_Y) != 0);
311
15.8k
    MEMPTR = val1 + 1;
312
15.8k
    return res16;
313
15.8k
}
314
315
// sbc16WithMEMPTR subtracts 16-bit value with carry from HL and sets MEMPTR
316
uint16_t Z80::sbc16WithMEMPTR(uint16_t a, uint16_t b)
317
15.8k
{
318
15.8k
    uint16_t result = sbc16(a, b);
319
15.8k
    MEMPTR = a + 1;
320
15.8k
    return result;
321
15.8k
}
322
323
// adc16 adds 16-bit value with carry to HL
324
uint16_t Z80::adc16(uint16_t val1, uint16_t val2)
325
0
{
326
0
    uint32_t carry = 0;
327
0
    if (GetFlag(FLAG_C))
328
0
    {
329
0
        carry = 1;
330
0
    }
331
0
    uint32_t result = (uint32_t)val1 + (uint32_t)val2 + carry;
332
0
    bool halfCarry = ((val1 & 0x0FFF) + (val2 & 0x0FFF) + (uint16_t)carry) > 0x0FFF;
333
0
    bool overflow = (((val1 ^ val2) & 0x8000) == 0) && (((val1 ^ (uint16_t)result) & 0x8000) != 0);
334
335
0
    uint16_t res16 = (uint16_t)result;
336
337
0
    SetFlag(FLAG_S, (res16 & 0x8000) != 0);
338
0
    SetFlag(FLAG_Z, res16 == 0);
339
0
    SetFlag(FLAG_H, halfCarry);
340
0
    SetFlag(FLAG_PV, overflow);
341
0
    SetFlag(FLAG_N, false);
342
0
    SetFlag(FLAG_C, result > 0xFFFF);
343
    // FIX: Set X and Y flags from high byte of result
344
0
    SetFlag(FLAG_X, (uint8_t(res16 >> 8) & FLAG_X) != 0);
345
0
    SetFlag(FLAG_Y, (uint8_t(res16 >> 8) & FLAG_Y) != 0);
346
0
    MEMPTR = val1 + 1;
347
348
0
    return res16;
349
0
}
350
351
// adc16WithMEMPTR adds 16-bit value with carry to HL and sets MEMPTR
352
uint16_t Z80::adc16WithMEMPTR(uint16_t a, uint16_t b)
353
0
{
354
0
    uint16_t result = adc16(a, b);
355
0
    MEMPTR = a + 1;
356
0
    return result;
357
0
}
358
359
// neg negates the accumulator
360
void Z80::neg()
361
670k
{
362
670k
    uint8_t value = A;
363
670k
    A = 0;
364
670k
    sub8(value);
365
670k
}
366
367
// retn returns from interrupt and restores IFF1 from IFF2
368
void Z80::retn()
369
0
{
370
0
    PC = Pop();
371
0
    MEMPTR = PC;
372
0
    IFF1 = IFF2;
373
0
}
374
375
// reti returns from interrupt (same as retn for Z80)
376
void Z80::reti()
377
0
{
378
0
    PC = Pop();
379
0
    MEMPTR = PC;
380
0
    IFF1 = IFF2;
381
0
}
382
383
// ldAI loads I register into A and updates flags
384
void Z80::ldAI()
385
0
{
386
0
    A = I;
387
0
    UpdateSZXYFlags(A);
388
0
    ClearFlag(FLAG_H);
389
0
    ClearFlag(FLAG_N);
390
0
    SetFlag(FLAG_PV, IFF2);
391
0
}
392
393
// ldAR loads R register into A and updates flags
394
void Z80::ldAR()
395
2.47k
{
396
    // Load the R register into A
397
2.47k
    A = R;
398
2.47k
    UpdateSZXYFlags(A);
399
2.47k
    ClearFlag(FLAG_H);
400
2.47k
    ClearFlag(FLAG_N);
401
2.47k
    SetFlag(FLAG_PV, IFF2);
402
2.47k
}
403
404
// rrd rotates digit between A and (HL) right
405
void Z80::rrd()
406
0
{
407
0
    uint8_t value = memory->ReadByte(HL);
408
0
    uint8_t ah = A & 0xF0;
409
0
    uint8_t al = A & 0x0F;
410
0
    uint8_t hl = value;
411
412
    // A bits 3-0 go to HL bits 7-4
413
    // HL bits 7-4 go to HL bits 3-0
414
    // HL bits 3-0 go to A bits 3-0
415
0
    A = ah | (hl & 0x0F);
416
0
    uint8_t newHL = ((hl & 0xF0) >> 4) | (al << 4);
417
0
    memory->WriteByte(HL, newHL);
418
419
0
    UpdateSZXYPVFlags(A);
420
0
    ClearFlag(FLAG_H);
421
0
    ClearFlag(FLAG_N);
422
423
    // Set MEMPTR = HL + 1
424
0
    MEMPTR = HL + 1;
425
0
}
426
427
// rld rotates digit between A and (HL) left
428
void Z80::rld()
429
0
{
430
0
    uint8_t value = memory->ReadByte(HL);
431
0
    uint8_t ah = A & 0xF0;
432
0
    uint8_t al = A & 0x0F;
433
0
    uint8_t hl = value;
434
435
    // A bits 3-0 go to HL bits 3-0
436
    // HL bits 3-0 go to HL bits 7-4
437
    // HL bits 7-4 go to A bits 3-0
438
0
    A = ah | (hl >> 4);
439
0
    uint8_t newHL = ((hl & 0x0F) << 4) | al;
440
0
    memory->WriteByte(HL, newHL);
441
442
0
    UpdateSZXYPVFlags(A);
443
0
    ClearFlag(FLAG_H);
444
0
    ClearFlag(FLAG_N);
445
446
    // Set MEMPTR = HL + 1
447
0
    MEMPTR = HL + 1;
448
0
}
449
450
// executeIN handles the IN r, (C) instructions
451
int Z80::executeIN(uint8_t reg)
452
2.12k
{
453
2.12k
    uint16_t bc = BC; // Save BC before doing anything
454
2.12k
    uint8_t value = inC();
455
456
    // Update flags
457
2.12k
    UpdateSZXYFlags(value);
458
2.12k
    ClearFlag(FLAG_H);
459
2.12k
    ClearFlag(FLAG_N);
460
    // Set PV flag based on parity of the result
461
2.12k
    SetFlag(FLAG_PV, parity(value));
462
    // MEMPTR = BC + 1 (using the original BC value)
463
2.12k
    MEMPTR = bc + 1;
464
465
    // Set the appropriate register
466
2.12k
    switch (reg)
467
2.12k
    {
468
0
    case 0:
469
0
        B = value;
470
0
        break;
471
0
    case 1:
472
0
        C = value;
473
0
        break;
474
1.51k
    case 2:
475
1.51k
        D = value;
476
1.51k
        break;
477
0
    case 3:
478
0
        E = value;
479
0
        break;
480
32
    case 4:
481
32
        H = value;
482
32
        break;
483
0
    case 5:
484
0
        L = value;
485
0
        break;
486
584
    case 7:
487
584
        A = value;
488
584
        break;
489
2.12k
    }
490
491
2.12k
    return 12;
492
2.12k
}
493
494
// executeOUT handles the OUT (C), r instructions
495
int Z80::executeOUT(uint8_t reg)
496
48.8k
{
497
48.8k
    uint8_t value;
498
499
    // Get the appropriate register value
500
48.8k
    switch (reg)
501
48.8k
    {
502
0
    case 0:
503
0
        value = B;
504
0
        break;
505
0
    case 1:
506
0
        value = C;
507
0
        break;
508
0
    case 2:
509
0
        value = D;
510
0
        break;
511
0
    case 3:
512
0
        value = E;
513
0
        break;
514
23.6k
    case 4:
515
23.6k
        value = H;
516
23.6k
        break;
517
32
    case 5:
518
32
        value = L;
519
32
        break;
520
25.1k
    case 7:
521
25.1k
        value = A;
522
25.1k
        break;
523
0
    default:
524
0
        value = 0;
525
0
        break;
526
48.8k
    }
527
528
48.8k
    outC(value);
529
    // MEMPTR = BC + 1
530
48.8k
    MEMPTR = BC + 1;
531
532
48.8k
    return 12;
533
48.8k
}
534
535
// ldi loads byte from (HL) to (DE), increments pointers, decrements BC
536
void Z80::ldi()
537
1.27M
{
538
1.27M
    uint8_t value = memory->ReadByte(HL);
539
1.27M
    memory->WriteByte(DE, value);
540
541
1.27M
    DE++;
542
1.27M
    HL++;
543
1.27M
    BC--;
544
545
    // FIXED: Calculate X and Y flags FIRST, preserving S, Z, C
546
1.27M
    uint8_t n = value + A;
547
1.27M
    F = (F & (FLAG_S | FLAG_Z | FLAG_C)) | (n & FLAG_X) | ((n & 0x02) << 4);
548
549
    // THEN set the other flags
550
1.27M
    ClearFlag(FLAG_H);
551
1.27M
    SetFlag(FLAG_PV, BC != 0);
552
1.27M
    ClearFlag(FLAG_N);
553
1.27M
}
554
555
// cpi compares A with (HL), increments HL, decrements BC
556
void Z80::cpi()
557
0
{
558
0
    uint8_t value = memory->ReadByte(HL);
559
0
    uint8_t result = A - value;
560
561
0
    HL++;
562
0
    BC--;
563
564
0
    SetFlag(FLAG_N, true);
565
0
    UpdateSZFlags(result);
566
567
    // Set H flag if borrow from bit 4
568
0
    SetFlag(FLAG_H, (A & 0x0F) < (value & 0x0F));
569
570
    // For CPI, F3 and F5 flags come from (A - (HL) - H_flag)
571
    // where H_flag is the half-carry flag AFTER the instruction
572
0
    uint8_t temp = result - (GetFlag(FLAG_H) ? 1 : 0);
573
0
    SetFlag(FLAG_X, (temp & 0x08) != 0); // Bit 3
574
0
    SetFlag(FLAG_Y, (temp & 0x02) != 0); // Bit 1
575
576
0
    if (BC != 0)
577
0
    {
578
0
        SetFlag(FLAG_PV, true);
579
0
    }
580
0
    else
581
0
    {
582
0
        ClearFlag(FLAG_PV);
583
0
    }
584
585
    // Set MEMPTR = PC - 1
586
0
    MEMPTR = PC - 1;
587
0
}
588
589
// ini inputs byte to (HL), increments HL, decrements B
590
void Z80::ini()
591
0
{
592
0
    uint8_t value = port->Read(uint16_t(C) | (uint16_t(B) << 8));
593
0
    memory->WriteByte(HL, value);
594
0
    HL++;
595
0
    uint16_t origbc = BC;
596
0
    B--;
597
598
    // Enhanced: Accurate flag calculation for INI
599
0
    int k = (int)value + (int)((C + 1) & 0xFF);
600
601
0
    SetFlag(FLAG_Z, B == 0);
602
0
    SetFlag(FLAG_S, (B & 0x80) != 0);
603
0
    SetFlag(FLAG_N, (value & 0x80) != 0);
604
0
    SetFlag(FLAG_H, k > 0xFF);
605
0
    SetFlag(FLAG_C, k > 0xFF);
606
    // P/V flag is parity of ((k & 0x07) XOR B)
607
0
    SetFlag(FLAG_PV, parity(uint8_t(k & 0x07) ^ B));
608
    // X and Y flags from B register
609
0
    F = (F & 0xD7) | (B & (FLAG_X | FLAG_Y));
610
611
0
    MEMPTR = origbc + 1;
612
0
}
613
614
// outi outputs byte from (HL) to port, increments HL, decrements B
615
void Z80::outi()
616
0
{
617
0
    uint8_t val = memory->ReadByte(HL);
618
0
    B--;
619
0
    port->Write(BC, val);
620
0
    HL++;
621
622
    // Enhanced: Accurate flag calculation for OUTI
623
    // Note: Use L after HL increment
624
0
    int k = (int)val + (int)L;
625
626
0
    SetFlag(FLAG_Z, B == 0);
627
0
    SetFlag(FLAG_S, (B & 0x80) != 0);
628
0
    SetFlag(FLAG_N, (val & 0x80) != 0);
629
0
    SetFlag(FLAG_H, k > 0xFF);
630
0
    SetFlag(FLAG_C, k > 0xFF);
631
    // P/V flag is parity of ((k & 0x07) XOR B)
632
0
    uint8_t pvVal = uint8_t(k & 0x07) ^ B;
633
0
    SetFlag(FLAG_PV, parity(pvVal));
634
    // X and Y flags from B register
635
0
    F = (F & 0xD7) | (B & (FLAG_X | FLAG_Y));
636
637
0
    MEMPTR = BC + 1;
638
0
}
639
640
// ldd loads byte from (HL) to (DE), decrements pointers, decrements BC
641
void Z80::ldd()
642
51.5k
{
643
51.5k
    uint8_t value = memory->ReadByte(HL);
644
51.5k
    memory->WriteByte(DE, value);
645
51.5k
    HL--;
646
51.5k
    DE--;
647
51.5k
    BC--;
648
649
    // FIXED: Calculate X and Y flags FIRST, preserving S, Z, C
650
51.5k
    uint8_t n = value + A;
651
51.5k
    F = (F & (FLAG_S | FLAG_Z | FLAG_C)) | (n & FLAG_X) | ((n & 0x02) << 4);
652
653
    // THEN set the other flags
654
51.5k
    ClearFlag(FLAG_H);
655
51.5k
    SetFlag(FLAG_PV, BC != 0);
656
51.5k
    ClearFlag(FLAG_N);
657
51.5k
}
658
659
// cpd compares A with (HL), decrements HL, decrements BC
660
void Z80::cpd()
661
0
{
662
0
    uint8_t val = memory->ReadByte(HL);
663
0
    int16_t result = (int16_t)A - (int16_t)val;
664
0
    HL--;
665
0
    BC--;
666
667
0
    SetFlag(FLAG_S, (uint8_t(result) & 0x80) != 0);
668
0
    SetFlag(FLAG_Z, uint8_t(result) == 0);
669
0
    SetFlag(FLAG_H, (int8_t(A & 0x0F) - int8_t(val & 0x0F)) < 0);
670
0
    SetFlag(FLAG_PV, BC != 0);
671
0
    SetFlag(FLAG_N, true);
672
673
    // Y flag calculation - preserve S, Z, H, PV, N, C flags
674
0
    uint8_t n = uint8_t(result);
675
0
    if (GetFlag(FLAG_H))
676
0
    {
677
0
        n--;
678
0
    }
679
0
    F = (F & (FLAG_S | FLAG_Z | FLAG_H | FLAG_PV | FLAG_N | FLAG_C)) | (n & FLAG_X) | ((n & 0x02) << 4);
680
0
    MEMPTR--;
681
0
}
682
683
// ind inputs byte to (HL), decrements HL, decrements B
684
void Z80::ind()
685
0
{
686
0
    uint8_t val = port->Read(BC);
687
0
    memory->WriteByte(HL, val);
688
0
    HL--;
689
0
    MEMPTR = BC - 1;
690
0
    B--;
691
692
    // Enhanced: Accurate flag calculation for IND
693
    // Note: Based on Z80 documentation, k = val + C (not C-1)
694
    // HUMAN: based on fuse test , ITS + C-1
695
    // k := int(val) + int(cpu.C)
696
697
0
    SetFlag(FLAG_Z, B == 0);
698
0
    SetFlag(FLAG_S, (B & 0x80) != 0);
699
0
    SetFlag(FLAG_N, (val & 0x80) != 0);
700
    // HUMAN : here was error
701
    // cpu.SetFlagState(FLAG_H, k > 0xFF)
702
    // cpu.SetFlagState(FLAG_C, k > 0xFF)
703
    // // P/V flag is parity of ((k & 0x07) XOR B)
704
    // pvVal := uint8(k&0x07) ^ cpu.B
705
    // cpu.SetFlagState(FLAG_PV, parity(pvVal))
706
707
0
    uint16_t diff = uint16_t(C - 1) + uint16_t(val);
708
0
    SetFlag(FLAG_H, diff > 0xFF);
709
0
    SetFlag(FLAG_C, diff > 0xFF);
710
0
    uint8_t temp = uint8_t((diff & 0x07) ^ uint16_t(B));
711
0
    uint8_t parity_val = 0;
712
0
    for (int i = 0; i < 8; i++)
713
0
    {
714
0
        parity_val ^= (temp >> i) & 1;
715
0
    }
716
0
    SetFlag(FLAG_PV, parity_val == 0);
717
718
    // X and Y flags from B register
719
0
    F = (F & 0xD7) | (B & (FLAG_X | FLAG_Y));
720
0
}
721
722
// outd outputs byte from (HL) to port, decrements HL, decrements B
723
void Z80::outd()
724
0
{
725
0
    uint8_t val = memory->ReadByte(HL);
726
0
    B--;
727
0
    port->Write(uint16_t(C) | (uint16_t(B) << 8), val);
728
0
    HL--;
729
730
0
    uint16_t k = uint16_t(val) + uint16_t(L);
731
732
0
    SetFlag(FLAG_Z, B == 0);
733
0
    SetFlag(FLAG_S, (B & 0x80) != 0);
734
0
    SetFlag(FLAG_N, (val & 0x80) != 0);
735
0
    SetFlag(FLAG_H, k > 0xFF);
736
0
    SetFlag(FLAG_C, k > 0xFF);
737
    // P/V flag is parity of ((k & 0x07) XOR B)
738
0
    uint8_t pvVal = uint8_t(k & 0x07) ^ B;
739
0
    SetFlag(FLAG_PV, parity(pvVal));
740
    // X and Y flags from B register
741
0
    F = (F & 0xD7) | (B & (FLAG_X | FLAG_Y));
742
743
0
    MEMPTR = BC - 1;
744
0
}
745
746
// ldir repeated LDI until BC=0
747
int Z80::ldir()
748
997k
{
749
997k
    ldi();
750
751
    // Add T-states for this iteration (21 for continuing, 16 for final)
752
997k
    if (BC != 0)
753
992k
    {
754
992k
        PC -= 2;
755
992k
        MEMPTR = PC + 1;
756
992k
        return 21;
757
992k
    }
758
4.70k
    else
759
4.70k
    {
760
4.70k
        return 16;
761
4.70k
    }
762
997k
}
763
764
// cpir repeated CPI until BC=0 or A=(HL)
765
int Z80::cpir()
766
0
{
767
0
    cpi();
768
    // printf("CPIR %x %x %x %x %s\n", A, BC, HL, memory->ReadByte(HL), GetFlag(FLAG_Z) ? "T" : "F");
769
0
    if (BC != 0 && !GetFlag(FLAG_Z))
770
0
    {
771
0
        PC -= 2; // Repeat instruction
772
773
        // Return T-states for continuing iteration
774
0
        return 21;
775
0
    }
776
0
    else
777
0
    {
778
        // Return T-states for final iteration
779
0
        MEMPTR = PC;
780
0
        return 16;
781
0
    }
782
0
}
783
784
// inir repeated INI until B=0
785
int Z80::inir()
786
0
{
787
0
    ini();
788
789
0
    if (B != 0)
790
0
    {
791
0
        PC -= 2; // Repeat instruction
792
        // Return T-states for continuing iteration
793
0
        return 21;
794
0
    }
795
0
    else
796
0
    {
797
        // Set MEMPTR to PC+1 at the end of the instruction
798
        // cpu.MEMPTR = cpu.PC
799
        // Return T-states for final iteration
800
0
        return 16;
801
0
    }
802
0
}
803
804
// otir repeated OUTI until B=0
805
int Z80::otir()
806
0
{
807
0
    outi();
808
809
0
    if (B != 0)
810
0
    {
811
0
        PC -= 2; // Repeat instruction
812
        // Return T-states for continuing iteration
813
0
        return 21;
814
0
    }
815
0
    else
816
0
    {
817
        // Return T-states for final iteration
818
0
        return 16;
819
0
    }
820
0
}
821
822
// lddr repeated LDD until BC=0
823
int Z80::lddr()
824
51.5k
{
825
    // Execute one LDD operation
826
51.5k
    ldd();
827
828
    // Add T-states for this iteration (21 for continuing, 16 for final)
829
51.5k
    if (BC != 0)
830
51.5k
    {
831
51.5k
        PC -= 2;
832
51.5k
        MEMPTR = PC + 1;
833
51.5k
        return 21;
834
51.5k
    }
835
10
    else
836
10
    {
837
10
        return 16;
838
10
    }
839
51.5k
}
840
841
// cpdr repeated CPD until BC=0 or A=(HL)
842
int Z80::cpdr()
843
0
{
844
0
    cpd();
845
846
0
    if (BC != 0 && !GetFlag(FLAG_Z))
847
0
    {
848
0
        PC -= 2; // Repeat instruction
849
        // Return T-states for continuing iteration
850
0
        MEMPTR = PC + 1;
851
0
        return 21;
852
0
    }
853
0
    else
854
0
    {
855
0
        MEMPTR = PC - 2;
856
        // Return T-states for final iteration
857
0
        return 16;
858
0
    }
859
0
}
860
861
// indr repeated IND until B=0
862
int Z80::indr()
863
0
{
864
0
    ind();
865
866
0
    if (B != 0)
867
0
    {
868
0
        PC -= 2; // Repeat instruction
869
        // Return T-states for continuing iteration
870
0
        return 21;
871
0
    }
872
0
    else
873
0
    {
874
        // Return T-states for final iteration
875
0
        return 16;
876
0
    }
877
0
}
878
879
// otdr repeated OUTD until B=0
880
int Z80::otdr()
881
0
{
882
0
    outd();
883
884
0
    if (B != 0)
885
0
    {
886
0
        PC -= 2; // Repeat instruction
887
        // Return T-states for continuing iteration
888
0
        return 21;
889
0
    }
890
0
    else
891
0
    {
892
0
        return 16;
893
0
    }
894
0
}
895
896
// inC reads from port (BC)
897
uint8_t Z80::inC()
898
2.12k
{
899
2.12k
    return port->Read(BC);
900
2.12k
}
901
902
// outC writes to port (BC)
903
void Z80::outC(uint8_t value)
904
48.8k
{
905
48.8k
    port->Write(BC, value);
906
48.8k
}
/Users/kiltum/projects/zxcpp/src/z80_fd_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
4
// Implementation of FD prefixed Z80 opcodes (IY instructions)
5
int Z80::ExecuteFDOpcode()
6
99.1k
{
7
    // Read the opcode from memory at the current program counter
8
99.1k
    uint8_t opcode = ReadOpcode();
9
    // R should not be incremented twice (already incremented in ExecuteOneInstruction for FD prefix)
10
    // R = (R & 0x80) | ((R - 1) & 0x7F);
11
99.1k
    R++;
12
99.1k
    switch (opcode)
13
99.1k
    {
14
    // Load instructions
15
7.56k
    case 0x09: // ADD IY, BC
16
7.56k
    {
17
7.56k
        uint16_t oldIY = IY;
18
7.56k
        uint16_t result = add16IY(IY, BC);
19
7.56k
        MEMPTR = oldIY + 1;
20
7.56k
        IY = result;
21
7.56k
    }
22
7.56k
        return 15;
23
64.7k
    case 0x19: // ADD IY, DE
24
64.7k
    {
25
64.7k
        uint16_t oldIY = IY;
26
64.7k
        uint16_t result = add16IY(IY, DE);
27
64.7k
        MEMPTR = oldIY + 1;
28
64.7k
        IY = result;
29
64.7k
    }
30
64.7k
        return 15;
31
12.2k
    case 0x21: // LD IY, nn
32
12.2k
        IY = ReadImmediateWord();
33
12.2k
        return 14;
34
840
    case 0x22: // LD (nn), IY
35
840
    {
36
840
        uint16_t addr = ReadImmediateWord();
37
840
        memory->WriteByte(addr, uint8_t(IY & 0xFF));
38
840
        memory->WriteByte(addr + 1, uint8_t((IY >> 8) & 0xFF));
39
840
        MEMPTR = addr + 1;
40
840
    }
41
840
        return 20;
42
3.17k
    case 0x23: // INC IY
43
3.17k
        IY++;
44
3.17k
        return 10;
45
0
    case 0x24: // INC IYH
46
0
        SetIYH(inc8(GetIYH()));
47
0
        return 8;
48
0
    case 0x25: // DEC IYH
49
0
        SetIYH(dec8(GetIYH()));
50
0
        return 8;
51
0
    case 0x26: // LD IYH, n
52
0
        SetIYH(ReadImmediateByte());
53
0
        return 11;
54
0
    case 0x29: // ADD IY, IY
55
0
    {
56
0
        uint16_t oldIY = IY;
57
0
        uint16_t result = add16IY(IY, IY);
58
0
        MEMPTR = oldIY + 1;
59
0
        IY = result;
60
0
    }
61
0
        return 15;
62
840
    case 0x2A: // LD IY, (nn)
63
840
    {
64
840
        uint16_t addr = ReadImmediateWord();
65
840
        IY = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
66
840
        MEMPTR = addr + 1;
67
840
    }
68
840
        return 20;
69
0
    case 0x2B: // DEC IY
70
0
        IY--;
71
0
        return 10;
72
0
    case 0x2C: // INC IYL
73
0
        SetIYL(inc8(GetIYL()));
74
0
        return 8;
75
0
    case 0x2D: // DEC IYL
76
0
        SetIYL(dec8(GetIYL()));
77
0
        return 8;
78
0
    case 0x2E: // LD IYL, n
79
0
        SetIYL(ReadImmediateByte());
80
0
        return 11;
81
8
    case 0x34: // INC (IY+d)
82
8
        return executeIncDecIndexedIY(true);
83
2
    case 0x35: // DEC (IY+d)
84
2
        return executeIncDecIndexedIY(false);
85
15
    case 0x36: // LD (IY+d), n
86
15
    {
87
15
        int8_t displacement = ReadDisplacement();
88
15
        uint8_t value = ReadImmediateByte();
89
15
        uint16_t addr = uint16_t(int32_t(IY) + int32_t(displacement));
90
15
        memory->WriteByte(addr, value);
91
15
        MEMPTR = addr;
92
15
    }
93
15
        return 19;
94
0
    case 0x39: // ADD IY, SP
95
0
    {
96
0
        uint16_t oldIY = IY;
97
0
        uint16_t result = add16IY(IY, SP);
98
0
        MEMPTR = oldIY + 1;
99
0
        IY = result;
100
0
    }
101
0
        return 15;
102
103
    // Load register from IY register
104
0
    case 0x44: // LD B, IYH
105
0
        B = GetIYH();
106
0
        return 8;
107
0
    case 0x45: // LD B, IYL
108
0
        B = GetIYL();
109
0
        return 8;
110
2
    case 0x46: // LD B, (IY+d)
111
2
        return executeLoadFromIndexedIY(0);
112
0
    case 0x4C: // LD C, IYH
113
0
        C = GetIYH();
114
0
        return 8;
115
0
    case 0x4D: // LD C, IYL
116
0
        C = GetIYL();
117
0
        return 8;
118
219
    case 0x4E: // LD C, (IY+d)
119
219
        return executeLoadFromIndexedIY(1);
120
0
    case 0x54: // LD D, IYH
121
0
        D = GetIYH();
122
0
        return 8;
123
0
    case 0x55: // LD D, IYL
124
0
        D = GetIYL();
125
0
        return 8;
126
1
    case 0x56: // LD D, (IY+d)
127
1
        return executeLoadFromIndexedIY(2);
128
0
    case 0x5C: // LD E, IYH
129
0
        E = GetIYH();
130
0
        return 8;
131
0
    case 0x5D: // LD E, IYL
132
0
        E = GetIYL();
133
0
        return 8;
134
0
    case 0x5E: // LD E, (IY+d)
135
0
        return executeLoadFromIndexedIY(3);
136
0
    case 0x60: // LD IYH, B
137
0
        SetIYH(B);
138
0
        return 8;
139
0
    case 0x61: // LD IYH, C
140
0
        SetIYH(C);
141
0
        return 8;
142
0
    case 0x62: // LD IYH, D
143
0
        SetIYH(D);
144
0
        return 8;
145
0
    case 0x63: // LD IYH, E
146
0
        SetIYH(E);
147
0
        return 8;
148
0
    case 0x64: // LD IYH, IYH
149
        // No operation needed
150
0
        return 8;
151
0
    case 0x65: // LD IYH, IYL
152
0
        SetIYH(GetIYL());
153
0
        return 8;
154
8
    case 0x66: // LD H, (IY+d)
155
8
        return executeLoadFromIndexedIY(4);
156
0
    case 0x67: // LD IYH, A
157
0
        SetIYH(A);
158
0
        return 8;
159
0
    case 0x68: // LD IYL, B
160
0
        SetIYL(B);
161
0
        return 8;
162
0
    case 0x69: // LD IYL, C
163
0
        SetIYL(C);
164
0
        return 8;
165
0
    case 0x6A: // LD IYL, D
166
0
        SetIYL(D);
167
0
        return 8;
168
0
    case 0x6B: // LD IYL, E
169
0
        SetIYL(E);
170
0
        return 8;
171
0
    case 0x6C: // LD IYL, IYH
172
0
        SetIYL(GetIYH());
173
0
        return 8;
174
0
    case 0x6D: // LD IYL, IYL
175
        // No operation needed
176
0
        return 8;
177
14
    case 0x6E: // LD L, (IY+d)
178
14
        return executeLoadFromIndexedIY(5);
179
0
    case 0x6F: // LD IYL, A
180
0
        SetIYL(A);
181
0
        return 8;
182
0
    case 0x70: // LD (IY+d), B
183
0
        return executeStoreToIndexedIY(B);
184
0
    case 0x71: // LD (IY+d), C
185
0
        return executeStoreToIndexedIY(C);
186
2
    case 0x72: // LD (IY+d), D
187
2
        return executeStoreToIndexedIY(D);
188
0
    case 0x73: // LD (IY+d), E
189
0
        return executeStoreToIndexedIY(E);
190
0
    case 0x74: // LD (IY+d), H
191
0
        return executeStoreToIndexedIY(H);
192
192
    case 0x75: // LD (IY+d), L
193
192
        return executeStoreToIndexedIY(L);
194
0
    case 0x77: // LD (IY+d), A
195
0
        return executeStoreToIndexedIY(A);
196
0
    case 0x7C: // LD A, IYH
197
0
        A = GetIYH();
198
0
        return 8;
199
0
    case 0x7D: // LD A, IYL
200
0
        A = GetIYL();
201
0
        return 8;
202
2.98k
    case 0x7E: // LD A, (IY+d)
203
2.98k
        return executeLoadFromIndexedIY(7);
204
205
    // Arithmetic and logic instructions
206
0
    case 0x84: // ADD A, IYH
207
0
        add8(GetIYH());
208
0
        return 8;
209
0
    case 0x85: // ADD A, IYL
210
0
        add8(GetIYL());
211
0
        return 8;
212
10
    case 0x86: // ADD A, (IY+d)
213
10
        return executeALUIndexedIY(0);
214
0
    case 0x8C: // ADC A, IYH
215
0
        adc8(GetIYH());
216
0
        return 8;
217
0
    case 0x8D: // ADC A, IYL
218
0
        adc8(GetIYL());
219
0
        return 8;
220
0
    case 0x8E: // ADC A, (IY+d)
221
0
        return executeALUIndexedIY(1);
222
0
    case 0x94: // SUB IYH
223
0
        sub8(GetIYH());
224
0
        return 8;
225
0
    case 0x95: // SUB IYL
226
0
        sub8(GetIYL());
227
0
        return 8;
228
1
    case 0x96: // SUB (IY+d)
229
1
        return executeALUIndexedIY(2);
230
0
    case 0x9C: // SBC A, IYH
231
0
        sbc8(GetIYH());
232
0
        return 8;
233
0
    case 0x9D: // SBC A, IYL
234
0
        sbc8(GetIYL());
235
0
        return 8;
236
0
    case 0x9E: // SBC A, (IY+d)
237
0
        return executeALUIndexedIY(3);
238
0
    case 0xA4: // AND IYH
239
0
        and8(GetIYH());
240
0
        return 8;
241
0
    case 0xA5: // AND IYL
242
0
        and8(GetIYL());
243
0
        return 8;
244
0
    case 0xA6: // AND (IY+d)
245
0
        return executeALUIndexedIY(4);
246
0
    case 0xAC: // XOR IYH
247
0
        xor8(GetIYH());
248
0
        return 8;
249
0
    case 0xAD: // XOR IYL
250
0
        xor8(GetIYL());
251
0
        return 8;
252
0
    case 0xAE: // XOR (IY+d)
253
0
        return executeALUIndexedIY(5);
254
0
    case 0xB4: // OR IYH
255
0
        or8(GetIYH());
256
0
        return 8;
257
0
    case 0xB5: // OR IYL
258
0
        or8(GetIYL());
259
0
        return 8;
260
0
    case 0xB6: // OR (IY+d)
261
0
        return executeALUIndexedIY(6);
262
0
    case 0xBC: // CP IYH
263
0
        cp8(GetIYH());
264
0
        return 8;
265
0
    case 0xBD: // CP IYL
266
0
        cp8(GetIYL());
267
0
        return 8;
268
19
    case 0xBE: // CP (IY+d)
269
19
        return executeALUIndexedIY(7);
270
271
    // POP and PUSH instructions
272
399
    case 0xE1: // POP IY
273
399
        IY = Pop();
274
399
        return 14;
275
0
    case 0xE3: // EX (SP), IY
276
0
    {
277
0
        uint16_t temp = (uint16_t(memory->ReadByte(SP + 1)) << 8) | uint16_t(memory->ReadByte(SP));
278
0
        memory->WriteByte(SP, uint8_t(IY & 0xFF));
279
0
        memory->WriteByte(SP + 1, uint8_t((IY >> 8) & 0xFF));
280
0
        IY = temp;
281
0
        MEMPTR = IY;
282
0
    }
283
0
        return 23;
284
3.30k
    case 0xE5: // PUSH IY
285
3.30k
        Push(IY);
286
3.30k
        return 15;
287
0
    case 0xE9: // JP (IY)
288
0
        PC = IY;
289
0
        return 8;
290
0
    case 0xF9: // LD SP, IY
291
0
        SP = IY;
292
0
        return 10;
293
294
    // Handle FD CB prefix (IY with displacement and CB operations)
295
2.60k
    case 0xCB: // FD CB prefix
296
2.60k
        return ExecuteFDCBOpcode();
297
298
0
    case 0x00: // Extended NOP (undocumented)
299
        // FD 00 is an undocumented instruction that acts as an extended NOP
300
        // It consumes the FD prefix and the 00 opcode but executes as a NOP
301
        // Takes 8 cycles total (4 for FD prefix fetch + 4 for 00 opcode fetch)
302
0
        return 8;
303
0
    default:
304
0
        PC--;
305
        // Unimplemented opcode - treat as regular opcode
306
        // This handles cases where FD is followed by a normal opcode
307
0
        return ExecuteOpcode();
308
99.1k
    }
309
99.1k
}
310
311
// add16IY adds two 16-bit values for IY register and updates flags
312
uint16_t Z80::add16IY(uint16_t a, uint16_t b)
313
72.3k
{
314
72.3k
    uint32_t result = (uint32_t)a + (uint32_t)b;
315
72.3k
    SetFlag(FLAG_C, result > 0xFFFF);
316
72.3k
    SetFlag(FLAG_H, (a & 0x0FFF) + (b & 0x0FFF) > 0x0FFF);
317
72.3k
    ClearFlag(FLAG_N);
318
    // For IY operations, we update X and Y flags from high byte of result
319
72.3k
    UpdateFlags3and5FromAddress((uint16_t)result);
320
72.3k
    return (uint16_t)result;
321
72.3k
}
322
323
// Get IYH (high byte of IY)
324
uint8_t Z80::GetIYH()
325
0
{
326
0
    return uint8_t(IY >> 8);
327
0
}
328
329
// Get IYL (low byte of IY)
330
uint8_t Z80::GetIYL()
331
0
{
332
0
    return uint8_t(IY & 0xFF);
333
0
}
334
335
// Set IYH (high byte of IY)
336
void Z80::SetIYH(uint8_t value)
337
0
{
338
0
    IY = (IY & 0x00FF) | (uint16_t(value) << 8);
339
0
}
340
341
// Set IYL (low byte of IY)
342
void Z80::SetIYL(uint8_t value)
343
0
{
344
0
    IY = (IY & 0xFF00) | uint16_t(value);
345
0
}
346
347
// executeIncDecIndexedIY handles INC/DEC (IY+d) instructions
348
int Z80::executeIncDecIndexedIY(bool isInc)
349
10
{
350
10
    int8_t displacement = ReadDisplacement();
351
10
    uint16_t addr = uint16_t(int32_t(IY) + int32_t(displacement));
352
10
    uint8_t value = memory->ReadByte(addr);
353
10
    uint8_t result;
354
10
    if (isInc)
355
8
    {
356
8
        result = inc8(value);
357
8
    }
358
2
    else
359
2
    {
360
2
        result = dec8(value);
361
2
    }
362
10
    memory->WriteByte(addr, result);
363
10
    MEMPTR = addr;
364
10
    return 23;
365
10
}
366
367
// executeLoadFromIndexedIY handles LD r, (IY+d) instructions
368
int Z80::executeLoadFromIndexedIY(uint8_t reg)
369
3.22k
{
370
3.22k
    int8_t displacement = ReadDisplacement();
371
3.22k
    uint16_t addr = uint16_t(int32_t(IY) + int32_t(displacement));
372
3.22k
    uint8_t value = memory->ReadByte(addr);
373
374
3.22k
    switch (reg)
375
3.22k
    {
376
2
    case 0:
377
2
        B = value;
378
2
        break;
379
219
    case 1:
380
219
        C = value;
381
219
        break;
382
1
    case 2:
383
1
        D = value;
384
1
        break;
385
0
    case 3:
386
0
        E = value;
387
0
        break;
388
8
    case 4:
389
8
        H = value;
390
8
        break;
391
14
    case 5:
392
14
        L = value;
393
14
        break;
394
2.98k
    case 7:
395
2.98k
        A = value;
396
2.98k
        break;
397
3.22k
    }
398
399
3.22k
    MEMPTR = addr;
400
3.22k
    return 19;
401
3.22k
}
402
403
// executeStoreToIndexedIY handles LD (IY+d), r instructions
404
int Z80::executeStoreToIndexedIY(uint8_t value)
405
194
{
406
194
    int8_t displacement = ReadDisplacement();
407
194
    uint16_t addr = uint16_t(int32_t(IY) + int32_t(displacement));
408
194
    memory->WriteByte(addr, value);
409
194
    MEMPTR = addr;
410
194
    return 19;
411
194
}
412
413
// executeALUIndexedIY handles ALU operations with (IY+d) operand
414
int Z80::executeALUIndexedIY(uint8_t opType)
415
30
{
416
30
    int8_t displacement = ReadDisplacement();
417
30
    uint16_t addr = uint16_t(int32_t(IY) + int32_t(displacement));
418
30
    uint8_t value = memory->ReadByte(addr);
419
420
30
    switch (opType)
421
30
    {
422
10
    case 0: // ADD
423
10
        add8(value);
424
10
        break;
425
0
    case 1: // ADC
426
0
        adc8(value);
427
0
        break;
428
1
    case 2: // SUB
429
1
        sub8(value);
430
1
        break;
431
0
    case 3: // SBC
432
0
        sbc8(value);
433
0
        break;
434
0
    case 4: // AND
435
0
        and8(value);
436
0
        break;
437
0
    case 5: // XOR
438
0
        xor8(value);
439
0
        break;
440
0
    case 6: // OR
441
0
        or8(value);
442
0
        break;
443
19
    case 7: // CP
444
19
        cp8(value);
445
19
        break;
446
30
    }
447
448
30
    MEMPTR = addr;
449
30
    return 19;
450
30
}
/Users/kiltum/projects/zxcpp/src/z80_fdcb_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
4
// Implementation of FDCB prefixed Z80 opcodes (IY + bit manipulation instructions)
5
int Z80::ExecuteFDCBOpcode()
6
2.60k
{
7
2.60k
    int8_t displacement = ReadDisplacement();
8
2.60k
    uint8_t opcode = ReadOpcode();
9
2.60k
    R--; // Decrement R because ReadOpcode() increments it
10
2.60k
    uint16_t addr = uint16_t(int32_t(IY) + int32_t(displacement));
11
2.60k
    uint8_t value = memory->ReadByte(addr);
12
2.60k
    MEMPTR = addr;
13
14
    // Handle rotate and shift instructions (0x00-0x3F)
15
2.60k
    if (opcode <= 0x3F)
16
0
    {
17
0
        return executeRotateShiftIndexedIY(opcode, addr, value);
18
0
    }
19
20
    // Handle bit test instructions (0x40-0x7F)
21
2.60k
    if (opcode >= 0x40 && opcode <= 0x7F)
22
2.54k
    {
23
2.54k
        uint8_t bitNum = uint8_t((opcode >> 3) & 0x07);
24
2.54k
        bitMem(bitNum, value, uint8_t(addr >> 8));
25
2.54k
        return 20;
26
2.54k
    }
27
28
    // Handle reset bit instructions (0x80-0xBF)
29
53
    if (opcode >= 0x80 && opcode <= 0xBF)
30
36
    {
31
36
        return executeResetBitIndexedIY(opcode, addr, value);
32
36
    }
33
34
    // Handle set bit instructions (0xC0-0xFF)
35
17
    if (opcode >= 0xC0)
36
17
    {
37
17
        return executeSetBitIndexedIY(opcode, addr, value);
38
17
    }
39
40
    // Unimplemented opcode
41
0
    return 23;
42
17
}
43
44
// executeRotateShiftIndexedIY handles rotate and shift instructions for IY indexed addressing
45
int Z80::executeRotateShiftIndexedIY(uint8_t opcode, uint16_t addr, uint8_t value)
46
0
{
47
    // Determine operation type from opcode bits 3-5
48
0
    uint8_t opType = (opcode >> 3) & 0x07;
49
    // Determine register from opcode bits 0-2
50
0
    uint8_t reg = opcode & 0x07;
51
52
    // Perform the operation
53
0
    uint8_t result;
54
0
    switch (opType)
55
0
    {
56
0
    case 0: // RLC
57
0
        result = rlc(value);
58
0
        break;
59
0
    case 1: // RRC
60
0
        result = rrc(value);
61
0
        break;
62
0
    case 2: // RL
63
0
        result = rl(value);
64
0
        break;
65
0
    case 3: // RR
66
0
        result = rr(value);
67
0
        break;
68
0
    case 4: // SLA
69
0
        result = sla(value);
70
0
        break;
71
0
    case 5: // SRA
72
0
        result = sra(value);
73
0
        break;
74
0
    case 6: // SLL (Undocumented)
75
0
        result = sll(value);
76
0
        break;
77
0
    case 7: // SRL
78
0
        result = srl(value);
79
0
        break;
80
0
    default:
81
0
        result = value;
82
0
        break;
83
0
    }
84
85
    // Store result in memory
86
0
    memory->WriteByte(addr, result);
87
88
    // Store result in register if needed (except for (HL) case)
89
0
    if (reg != 6)
90
0
    { // reg 6 is (HL) - no register store needed
91
0
        switch (reg)
92
0
        {
93
0
        case 0:
94
0
            B = result;
95
0
            break;
96
0
        case 1:
97
0
            C = result;
98
0
            break;
99
0
        case 2:
100
0
            D = result;
101
0
            break;
102
0
        case 3:
103
0
            E = result;
104
0
            break;
105
0
        case 4:
106
0
            H = result;
107
0
            break;
108
0
        case 5:
109
0
            L = result;
110
0
            break;
111
0
        case 7:
112
0
            A = result;
113
0
            break;
114
0
        }
115
0
    }
116
117
0
    return 23;
118
0
}
119
120
// executeResetBitIndexedIY handles reset bit instructions for IY indexed addressing
121
int Z80::executeResetBitIndexedIY(uint8_t opcode, uint16_t addr, uint8_t value)
122
36
{
123
36
    uint8_t bitNum = uint8_t((opcode >> 3) & 0x07);
124
36
    uint8_t reg = opcode & 0x07;
125
126
36
    uint8_t result = res(bitNum, value);
127
36
    memory->WriteByte(addr, result);
128
129
    // Store result in register if needed (except for (HL) case)
130
36
    if (reg != 6)
131
0
    { // reg 6 is (HL) - no register store needed
132
0
        switch (reg)
133
0
        {
134
0
        case 0:
135
0
            B = result;
136
0
            break;
137
0
        case 1:
138
0
            C = result;
139
0
            break;
140
0
        case 2:
141
0
            D = result;
142
0
            break;
143
0
        case 3:
144
0
            E = result;
145
0
            break;
146
0
        case 4:
147
0
            H = result;
148
0
            break;
149
0
        case 5:
150
0
            L = result;
151
0
            break;
152
0
        case 7:
153
0
            A = result;
154
0
            break;
155
0
        }
156
0
    }
157
158
36
    return 23;
159
36
}
160
161
// executeSetBitIndexedIY handles set bit instructions for IY indexed addressing
162
int Z80::executeSetBitIndexedIY(uint8_t opcode, uint16_t addr, uint8_t value)
163
17
{
164
17
    uint8_t bitNum = uint8_t((opcode >> 3) & 0x07);
165
17
    uint8_t reg = opcode & 0x07;
166
167
17
    uint8_t result = set(bitNum, value);
168
17
    memory->WriteByte(addr, result);
169
170
    // Store result in register if needed (except for (HL) case)
171
17
    if (reg != 6)
172
0
    { // reg 6 is (HL) - no register store needed
173
0
        switch (reg)
174
0
        {
175
0
        case 0:
176
0
            B = result;
177
0
            break;
178
0
        case 1:
179
0
            C = result;
180
0
            break;
181
0
        case 2:
182
0
            D = result;
183
0
            break;
184
0
        case 3:
185
0
            E = result;
186
0
            break;
187
0
        case 4:
188
0
            H = result;
189
0
            break;
190
0
        case 5:
191
0
            L = result;
192
0
            break;
193
0
        case 7:
194
0
            A = result;
195
0
            break;
196
0
        }
197
0
    }
198
199
17
    return 23;
200
17
}
/Users/kiltum/projects/zxcpp/src/z80_opcodes.cpp
Line
Count
Source
1
#include "z80.hpp"
2
#include "memory.hpp"
3
#include "port.hpp"
4
5
// Implementation of common Z80 opcodes
6
int Z80::ExecuteOpcode()
7
163M
{
8
    // Read the opcode from memory at the current program counter
9
163M
    uint8_t opcode = ReadOpcode();
10
11
163M
    switch (opcode)
12
163M
    {
13
    // 8-bit load group
14
3
    case 0x00: // NOP
15
3
        return 4;
16
370k
    case 0x01: // LD BC, nn
17
370k
        BC = ReadImmediateWord();
18
370k
        return 10;
19
0
    case 0x02: // LD (BC), A
20
0
        memory->WriteByte(BC, A);
21
0
        MEMPTR = (uint16_t(A) << 8) | (uint16_t(BC + 1) & 0xff);
22
0
        return 7;
23
3.08k
    case 0x03: // INC BC
24
3.08k
        BC++;
25
3.08k
        return 6;
26
9.95M
    case 0x04: // INC B
27
9.95M
        B = inc8(B);
28
9.95M
        return 4;
29
75
    case 0x05: // DEC B
30
75
        B = dec8(B);
31
75
        return 4;
32
1.15M
    case 0x06: // LD B, n
33
1.15M
        B = ReadImmediateByte();
34
1.15M
        return 7;
35
3.24k
    case 0x07: // RLCA
36
3.24k
        rlca();
37
3.24k
        return 4;
38
171k
    case 0x08: // EX AF, AF'
39
171k
    {
40
171k
        uint16_t temp = AF;
41
171k
        AF = AF_;
42
171k
        AF_ = temp;
43
171k
    }
44
171k
        return 4;
45
327k
    case 0x09: // ADD HL, BC
46
327k
    {
47
327k
        uint16_t result = add16(HL, BC);
48
327k
        MEMPTR = HL + 1;
49
327k
        HL = result;
50
327k
    }
51
327k
        return 11;
52
3.10k
    case 0x0A: // LD A, (BC)
53
3.10k
        A = memory->ReadByte(BC);
54
3.10k
        MEMPTR = BC + 1;
55
3.10k
        return 7;
56
32.6k
    case 0x0B: // DEC BC
57
32.6k
        BC--;
58
32.6k
        return 6;
59
15
    case 0x0C: // INC C
60
15
        C = inc8(C);
61
15
        return 4;
62
1.43k
    case 0x0D: // DEC C
63
1.43k
        C = dec8(C);
64
1.43k
        return 4;
65
20.5k
    case 0x0E: // LD C, n
66
20.5k
        C = ReadImmediateByte();
67
20.5k
        return 7;
68
409k
    case 0x0F: // RRCA
69
409k
        rrca();
70
409k
        return 4;
71
11.4M
    case 0x10: // DJNZ e
72
11.4M
        B--;
73
11.4M
        if (B != 0)
74
10.6M
        {
75
10.6M
            int8_t offset = ReadDisplacement();
76
10.6M
            MEMPTR = PC + uint16_t(int32_t(offset));
77
10.6M
            PC = uint16_t(int32_t(PC) + int32_t(offset));
78
10.6M
            return 13;
79
10.6M
        }
80
770k
        PC++; // Skip the offset byte
81
770k
        return 8;
82
320k
    case 0x11: // LD DE, nn
83
320k
        DE = ReadImmediateWord();
84
320k
        return 10;
85
66.4k
    case 0x12: // LD (DE), A
86
66.4k
        memory->WriteByte(DE, A);
87
66.4k
        MEMPTR = (uint16_t(A) << 8) | (uint16_t(DE + 1) & 0xff);
88
66.4k
        return 7;
89
808k
    case 0x13: // INC DE
90
808k
        DE++;
91
808k
        return 6;
92
53.2k
    case 0x14: // INC D
93
53.2k
        D = inc8(D);
94
53.2k
        return 4;
95
8.06k
    case 0x15: // DEC D
96
8.06k
        D = dec8(D);
97
8.06k
        return 4;
98
114k
    case 0x16: // LD D, n
99
114k
        D = ReadImmediateByte();
100
114k
        return 7;
101
54.9k
    case 0x17: // RLA
102
54.9k
        rla();
103
54.9k
        return 4;
104
298k
    case 0x18: // JR e
105
298k
    {
106
298k
        int8_t offset = ReadDisplacement();
107
298k
        MEMPTR = PC + uint16_t(int32_t(offset));
108
298k
        PC = uint16_t(int32_t(PC) + int32_t(offset));
109
298k
    }
110
298k
        return 12;
111
238k
    case 0x19: // ADD HL, DE
112
238k
    {
113
238k
        uint16_t result = add16(HL, DE);
114
238k
        MEMPTR = HL + 1;
115
238k
        HL = result;
116
238k
    }
117
238k
        return 11;
118
1.14M
    case 0x1A: // LD A, (DE)
119
1.14M
        A = memory->ReadByte(DE);
120
1.14M
        MEMPTR = DE + 1;
121
1.14M
        return 7;
122
42.8k
    case 0x1B: // DEC DE
123
42.8k
        DE--;
124
42.8k
        return 6;
125
331k
    case 0x1C: // INC E
126
331k
        E = inc8(E);
127
331k
        return 4;
128
10.4k
    case 0x1D: // DEC E
129
10.4k
        E = dec8(E);
130
10.4k
        return 4;
131
933
    case 0x1E: // LD E, n
132
933
        E = ReadImmediateByte();
133
933
        return 7;
134
9.96M
    case 0x1F: // RRA
135
9.96M
        rra();
136
9.96M
        return 4;
137
1.97M
    case 0x20: // JR NZ, e
138
1.97M
        if (!GetFlag(FLAG_Z))
139
1.55M
        {
140
1.55M
            int8_t offset = ReadDisplacement();
141
1.55M
            MEMPTR = PC + uint16_t(int32_t(offset));
142
1.55M
            PC = uint16_t(int32_t(PC) + int32_t(offset));
143
1.55M
            return 12;
144
1.55M
        }
145
418k
        PC++; // Skip the offset byte
146
418k
        return 7;
147
928k
    case 0x21: // LD HL, nn
148
928k
        HL = ReadImmediateWord();
149
928k
        return 10;
150
89.6k
    case 0x22: // LD (nn), HL
151
89.6k
    {
152
89.6k
        uint16_t addr = ReadImmediateWord();
153
89.6k
        memory->WriteByte(addr, uint8_t(HL & 0xFF));
154
89.6k
        memory->WriteByte(addr + 1, uint8_t((HL >> 8) & 0xFF));
155
89.6k
        MEMPTR = addr + 1;
156
89.6k
    }
157
89.6k
        return 16;
158
4.27M
    case 0x23: // INC HL
159
4.27M
        HL++;
160
4.27M
        return 6;
161
1.10M
    case 0x24: // INC H
162
1.10M
        H = inc8(H);
163
1.10M
        return 4;
164
215
    case 0x25: // DEC H
165
215
        H = dec8(H);
166
215
        return 4;
167
174k
    case 0x26: // LD H, n
168
174k
        H = ReadImmediateByte();
169
174k
        return 7;
170
0
    case 0x27: // DAA
171
0
        daa();
172
0
        return 4;
173
12.4M
    case 0x28: // JR Z, e
174
12.4M
        if (GetFlag(FLAG_Z))
175
10.1M
        {
176
10.1M
            int8_t offset = ReadDisplacement();
177
10.1M
            MEMPTR = uint16_t(int32_t(PC) + int32_t(offset));
178
10.1M
            PC = uint16_t(int32_t(PC) + int32_t(offset));
179
10.1M
            return 12;
180
10.1M
        }
181
2.31M
        ReadDisplacement(); // Skip the offset byte
182
2.31M
        return 7;
183
292k
    case 0x29: // ADD HL, HL
184
292k
    {
185
292k
        uint16_t result = add16(HL, HL);
186
292k
        MEMPTR = HL + 1;
187
292k
        HL = result;
188
292k
    }
189
292k
        return 11;
190
109k
    case 0x2A: // LD HL, (nn)
191
109k
    {
192
109k
        uint16_t addr = ReadImmediateWord();
193
109k
        HL = (uint16_t(memory->ReadByte(addr + 1)) << 8) | uint16_t(memory->ReadByte(addr));
194
109k
        MEMPTR = addr + 1;
195
109k
    }
196
109k
        return 16;
197
41.8k
    case 0x2B: // DEC HL
198
41.8k
        HL--;
199
41.8k
        return 6;
200
1.28M
    case 0x2C: // INC L
201
1.28M
        L = inc8(L);
202
1.28M
        return 4;
203
1.13M
    case 0x2D: // DEC L
204
1.13M
        L = dec8(L);
205
1.13M
        return 4;
206
66.2k
    case 0x2E: // LD L, n
207
66.2k
        L = ReadImmediateByte();
208
66.2k
        return 7;
209
726k
    case 0x2F: // CPL
210
726k
        cpl();
211
726k
        return 4;
212
801k
    case 0x30: // JR NC, e
213
801k
        if (!GetFlag(FLAG_C))
214
657k
        {
215
657k
            int8_t offset = ReadDisplacement();
216
657k
            MEMPTR = PC + uint16_t(int32_t(offset));
217
657k
            PC = uint16_t(int32_t(PC) + int32_t(offset));
218
657k
            return 12;
219
657k
        }
220
143k
        PC++; // Skip the offset byte
221
143k
        return 7;
222
13.0k
    case 0x31: // LD SP, nn
223
13.0k
        SP = ReadImmediateWord();
224
13.0k
        return 10;
225
2.56M
    case 0x32: // LD (nn), A
226
2.56M
    {
227
2.56M
        uint16_t addr = ReadImmediateWord();
228
2.56M
        memory->WriteByte(addr, A);
229
2.56M
        MEMPTR = (uint16_t(A) << 8) | ((addr + 1) & 0xFF);
230
2.56M
    }
231
2.56M
        return 13;
232
1
    case 0x33: // INC SP
233
1
        SP++;
234
1
        return 6;
235
80.5k
    case 0x34: // INC (HL)
236
80.5k
    {
237
80.5k
        uint8_t value = memory->ReadByte(HL);
238
80.5k
        uint8_t result = inc8(value);
239
80.5k
        memory->WriteByte(HL, result);
240
80.5k
    }
241
80.5k
        return 11;
242
712k
    case 0x35: // DEC (HL)
243
712k
    {
244
712k
        uint8_t value = memory->ReadByte(HL);
245
712k
        uint8_t result = dec8(value);
246
712k
        memory->WriteByte(HL, result);
247
712k
    }
248
712k
        return 11;
249
26.1k
    case 0x36: // LD (HL), n
250
26.1k
    {
251
26.1k
        uint8_t value = ReadImmediateByte();
252
26.1k
        memory->WriteByte(HL, value);
253
26.1k
    }
254
26.1k
        return 10;
255
718k
    case 0x37: // SCF
256
718k
        scf();
257
718k
        return 4;
258
132k
    case 0x38: // JR C, e
259
132k
        if (GetFlag(FLAG_C))
260
33.2k
        {
261
33.2k
            int8_t offset = ReadDisplacement();
262
33.2k
            MEMPTR = PC + uint16_t(int32_t(offset));
263
33.2k
            PC = uint16_t(int32_t(PC) + int32_t(offset));
264
33.2k
            return 12;
265
33.2k
        }
266
99.0k
        PC++; // Skip the offset byte
267
99.0k
        return 7;
268
0
    case 0x39: // ADD HL, SP
269
0
    {
270
0
        uint16_t result = add16(HL, SP);
271
0
        MEMPTR = HL + 1;
272
0
        HL = result;
273
0
    }
274
0
        return 11;
275
90.7k
    case 0x3A: // LD A, (nn)
276
90.7k
    {
277
90.7k
        uint16_t addr = ReadImmediateWord();
278
90.7k
        A = memory->ReadByte(addr);
279
90.7k
        MEMPTR = addr + 1;
280
90.7k
    }
281
90.7k
        return 13;
282
227k
    case 0x3B: // DEC SP
283
227k
        SP--;
284
227k
        return 6;
285
51.0k
    case 0x3C: // INC A
286
51.0k
        A = inc8(A);
287
51.0k
        return 4;
288
820k
    case 0x3D: // DEC A
289
820k
        A = dec8(A);
290
820k
        return 4;
291
13.0M
    case 0x3E: // LD A, n
292
13.0M
        A = ReadImmediateByte();
293
13.0M
        return 7;
294
56
    case 0x3F: // CCF
295
56
        ccf();
296
56
        return 4;
297
298
    // LD r, r' instructions
299
0
    case 0x40: // LD B, B
300
0
        return 4;
301
0
    case 0x41: // LD B, C
302
0
        B = C;
303
0
        return 4;
304
1.58k
    case 0x42: // LD B, D
305
1.58k
        B = D;
306
1.58k
        return 4;
307
34
    case 0x43: // LD B, E
308
34
        B = E;
309
34
        return 4;
310
20.5k
    case 0x44: // LD B, H
311
20.5k
        B = H;
312
20.5k
        return 4;
313
0
    case 0x45: // LD B, L
314
0
        B = L;
315
0
        return 4;
316
133k
    case 0x46: // LD B, (HL)
317
133k
        B = memory->ReadByte(HL);
318
133k
        return 7;
319
29.5k
    case 0x47: // LD B, A
320
29.5k
        B = A;
321
29.5k
        return 4;
322
0
    case 0x48: // LD C, B
323
0
        C = B;
324
0
        return 4;
325
0
    case 0x49: // LD C, C
326
0
        return 4;
327
8.43k
    case 0x4A: // LD C, D
328
8.43k
        C = D;
329
8.43k
        return 4;
330
5.73k
    case 0x4B: // LD C, E
331
5.73k
        C = E;
332
5.73k
        return 4;
333
675
    case 0x4C: // LD C, H
334
675
        C = H;
335
675
        return 4;
336
21.2k
    case 0x4D: // LD C, L
337
21.2k
        C = L;
338
21.2k
        return 4;
339
158k
    case 0x4E: // LD C, (HL)
340
158k
        C = memory->ReadByte(HL);
341
158k
        return 7;
342
790k
    case 0x4F: // LD C, A
343
790k
        C = A;
344
790k
        return 4;
345
18.9k
    case 0x50: // LD D, B
346
18.9k
        D = B;
347
18.9k
        return 4;
348
4.03k
    case 0x51: // LD D, C
349
4.03k
        D = C;
350
4.03k
        return 4;
351
0
    case 0x52: // LD D, D
352
0
        return 4;
353
3
    case 0x53: // LD D, E
354
3
        D = E;
355
3
        return 4;
356
9.60k
    case 0x54: // LD D, H
357
9.60k
        D = H;
358
9.60k
        return 4;
359
256
    case 0x55: // LD D, L
360
256
        D = L;
361
256
        return 4;
362
551k
    case 0x56: // LD D, (HL)
363
551k
        D = memory->ReadByte(HL);
364
551k
        return 7;
365
38.8k
    case 0x57: // LD D, A
366
38.8k
        D = A;
367
38.8k
        return 4;
368
131
    case 0x58: // LD E, B
369
131
        E = B;
370
131
        return 4;
371
14.5k
    case 0x59: // LD E, C
372
14.5k
        E = C;
373
14.5k
        return 4;
374
0
    case 0x5A: // LD E, D
375
0
        E = D;
376
0
        return 4;
377
0
    case 0x5B: // LD E, E
378
0
        return 4;
379
0
    case 0x5C: // LD E, H
380
0
        E = H;
381
0
        return 4;
382
9.60k
    case 0x5D: // LD E, L
383
9.60k
        E = L;
384
9.60k
        return 4;
385
612k
    case 0x5E: // LD E, (HL)
386
612k
        E = memory->ReadByte(HL);
387
612k
        return 7;
388
120k
    case 0x5F: // LD E, A
389
120k
        E = A;
390
120k
        return 4;
391
1.11k
    case 0x60: // LD H, B
392
1.11k
        H = B;
393
1.11k
        return 4;
394
23.7k
    case 0x61: // LD H, C
395
23.7k
        H = C;
396
23.7k
        return 4;
397
8
    case 0x62: // LD H, D
398
8
        H = D;
399
8
        return 4;
400
0
    case 0x63: // LD H, E
401
0
        H = E;
402
0
        return 4;
403
0
    case 0x64: // LD H, H
404
0
        return 4;
405
0
    case 0x65: // LD H, L
406
0
        H = L;
407
0
        return 4;
408
5.08k
    case 0x66: // LD H, (HL)
409
5.08k
        H = memory->ReadByte(HL);
410
5.08k
        return 7;
411
250k
    case 0x67: // LD H, A
412
250k
        H = A;
413
250k
        return 4;
414
32
    case 0x68: // LD L, B
415
32
        L = B;
416
32
        return 4;
417
1.11k
    case 0x69: // LD L, C
418
1.11k
        L = C;
419
1.11k
        return 4;
420
70.6k
    case 0x6A: // LD L, D
421
70.6k
        L = D;
422
70.6k
        return 4;
423
5.35k
    case 0x6B: // LD L, E
424
5.35k
        L = E;
425
5.35k
        return 4;
426
0
    case 0x6C: // LD L, H
427
0
        L = H;
428
0
        return 4;
429
0
    case 0x6D: // LD L, L
430
0
        return 4;
431
70.6k
    case 0x6E: // LD L, (HL)
432
70.6k
        L = memory->ReadByte(HL);
433
70.6k
        return 7;
434
275k
    case 0x6F: // LD L, A
435
275k
        L = A;
436
275k
        return 4;
437
37.5k
    case 0x70: // LD (HL), B
438
37.5k
        memory->WriteByte(HL, B);
439
37.5k
        return 7;
440
106k
    case 0x71: // LD (HL), C
441
106k
        memory->WriteByte(HL, C);
442
106k
        return 7;
443
236k
    case 0x72: // LD (HL), D
444
236k
        memory->WriteByte(HL, D);
445
236k
        return 7;
446
241k
    case 0x73: // LD (HL), E
447
241k
        memory->WriteByte(HL, E);
448
241k
        return 7;
449
0
    case 0x74: // LD (HL), H
450
0
        memory->WriteByte(HL, H);
451
0
        return 7;
452
0
    case 0x75: // LD (HL), L
453
0
        memory->WriteByte(HL, L);
454
0
        return 7;
455
0
    case 0x76: // HALT
456
0
        HALT = true;
457
0
        PC--;
458
0
        return 4;
459
2.56M
    case 0x77: // LD (HL), A
460
2.56M
        memory->WriteByte(HL, A);
461
2.56M
        return 7;
462
78.1k
    case 0x78: // LD A, B
463
78.1k
        A = B;
464
78.1k
        return 4;
465
1.48M
    case 0x79: // LD A, C
466
1.48M
        A = C;
467
1.48M
        return 4;
468
745k
    case 0x7A: // LD A, D
469
745k
        A = D;
470
745k
        return 4;
471
947k
    case 0x7B: // LD A, E
472
947k
        A = E;
473
947k
        return 4;
474
912k
    case 0x7C: // LD A, H
475
912k
        A = H;
476
912k
        return 4;
477
105k
    case 0x7D: // LD A, L
478
105k
        A = L;
479
105k
        return 4;
480
1.73M
    case 0x7E: // LD A, (HL)
481
1.73M
        A = memory->ReadByte(HL);
482
1.73M
        return 7;
483
0
    case 0x7F: // LD A, A
484
0
        return 4;
485
486
    // Arithmetic and logic group
487
403
    case 0x80: // ADD A, B
488
403
        add8(B);
489
403
        return 4;
490
1.81k
    case 0x81: // ADD A, C
491
1.81k
        add8(C);
492
1.81k
        return 4;
493
4.52k
    case 0x82: // ADD A, D
494
4.52k
        add8(D);
495
4.52k
        return 4;
496
38.3k
    case 0x83: // ADD A, E
497
38.3k
        add8(E);
498
38.3k
        return 4;
499
0
    case 0x84: // ADD A, H
500
0
        add8(H);
501
0
        return 4;
502
70.6k
    case 0x85: // ADD A, L
503
70.6k
        add8(L);
504
70.6k
        return 4;
505
12.4k
    case 0x86: // ADD A, (HL)
506
12.4k
    {
507
12.4k
        uint8_t value = memory->ReadByte(HL);
508
12.4k
        add8(value);
509
12.4k
    }
510
12.4k
        return 7;
511
253k
    case 0x87: // ADD A, A
512
253k
        add8(A);
513
253k
        return 4;
514
0
    case 0x88: // ADC A, B
515
0
        adc8(B);
516
0
        return 4;
517
9
    case 0x89: // ADC A, C
518
9
        adc8(C);
519
9
        return 4;
520
0
    case 0x8A: // ADC A, D
521
0
        adc8(D);
522
0
        return 4;
523
0
    case 0x8B: // ADC A, E
524
0
        adc8(E);
525
0
        return 4;
526
0
    case 0x8C: // ADC A, H
527
0
        adc8(H);
528
0
        return 4;
529
0
    case 0x8D: // ADC A, L
530
0
        adc8(L);
531
0
        return 4;
532
0
    case 0x8E: // ADC A, (HL)
533
0
    {
534
0
        uint8_t value = memory->ReadByte(HL);
535
0
        adc8(value);
536
0
    }
537
0
        return 7;
538
0
    case 0x8F: // ADC A, A
539
0
        adc8(A);
540
0
        return 4;
541
4.85k
    case 0x90: // SUB B
542
4.85k
        sub8(B);
543
4.85k
        return 4;
544
3.86k
    case 0x91: // SUB C
545
3.86k
        sub8(C);
546
3.86k
        return 4;
547
1.05k
    case 0x92: // SUB D
548
1.05k
        sub8(D);
549
1.05k
        return 4;
550
20.1k
    case 0x93: // SUB E
551
20.1k
        sub8(E);
552
20.1k
        return 4;
553
0
    case 0x94: // SUB H
554
0
        sub8(H);
555
0
        return 4;
556
0
    case 0x95: // SUB L
557
0
        sub8(L);
558
0
        return 4;
559
0
    case 0x96: // SUB (HL)
560
0
    {
561
0
        uint8_t value = memory->ReadByte(HL);
562
0
        sub8(value);
563
0
    }
564
0
        return 7;
565
0
    case 0x97: // SUB A
566
0
        sub8(A);
567
0
        return 4;
568
0
    case 0x98: // SBC A, B
569
0
        sbc8(B);
570
0
        return 4;
571
0
    case 0x99: // SBC A, C
572
0
        sbc8(C);
573
0
        return 4;
574
0
    case 0x9A: // SBC A, D
575
0
        sbc8(D);
576
0
        return 4;
577
0
    case 0x9B: // SBC A, E
578
0
        sbc8(E);
579
0
        return 4;
580
0
    case 0x9C: // SBC A, H
581
0
        sbc8(H);
582
0
        return 4;
583
0
    case 0x9D: // SBC A, L
584
0
        sbc8(L);
585
0
        return 4;
586
0
    case 0x9E: // SBC A, (HL)
587
0
    {
588
0
        uint8_t value = memory->ReadByte(HL);
589
0
        sbc8(value);
590
0
    }
591
0
        return 7;
592
252
    case 0x9F: // SBC A, A
593
252
        sbc8(A);
594
252
        return 4;
595
8.84k
    case 0xA0: // AND B
596
8.84k
        and8(B);
597
8.84k
        return 4;
598
747k
    case 0xA1: // AND C
599
747k
        and8(C);
600
747k
        return 4;
601
413
    case 0xA2: // AND D
602
413
        and8(D);
603
413
        return 4;
604
0
    case 0xA3: // AND E
605
0
        and8(E);
606
0
        return 4;
607
0
    case 0xA4: // AND H
608
0
        and8(H);
609
0
        return 4;
610
0
    case 0xA5: // AND L
611
0
        and8(L);
612
0
        return 4;
613
106k
    case 0xA6: // AND (HL)
614
106k
    {
615
106k
        uint8_t value = memory->ReadByte(HL);
616
106k
        and8(value);
617
106k
    }
618
106k
        return 7;
619
695k
    case 0xA7: // AND A
620
695k
        and8(A);
621
695k
        return 4;
622
1.03k
    case 0xA8: // XOR B
623
1.03k
        xor8(B);
624
1.03k
        return 4;
625
9.94M
    case 0xA9: // XOR C
626
9.94M
        xor8(C);
627
9.94M
        return 4;
628
0
    case 0xAA: // XOR D
629
0
        xor8(D);
630
0
        return 4;
631
826
    case 0xAB: // XOR E
632
826
        xor8(E);
633
826
        return 4;
634
7.18k
    case 0xAC: // XOR H
635
7.18k
        xor8(H);
636
7.18k
        return 4;
637
43.0k
    case 0xAD: // XOR L
638
43.0k
        xor8(L);
639
43.0k
        return 4;
640
2.02M
    case 0xAE: // XOR (HL)
641
2.02M
    {
642
2.02M
        uint8_t value = memory->ReadByte(HL);
643
2.02M
        xor8(value);
644
2.02M
    }
645
2.02M
        return 7;
646
369k
    case 0xAF: // XOR A
647
369k
        xor8(A);
648
369k
        return 4;
649
1.53k
    case 0xB0: // OR B
650
1.53k
        or8(B);
651
1.53k
        return 4;
652
33.0k
    case 0xB1: // OR C
653
33.0k
        or8(C);
654
33.0k
        return 4;
655
0
    case 0xB2: // OR D
656
0
        or8(D);
657
0
        return 4;
658
42.5k
    case 0xB3: // OR E
659
42.5k
        or8(E);
660
42.5k
        return 4;
661
2.18k
    case 0xB4: // OR H
662
2.18k
        or8(H);
663
2.18k
        return 4;
664
8.42k
    case 0xB5: // OR L
665
8.42k
        or8(L);
666
8.42k
        return 4;
667
14.0k
    case 0xB6: // OR (HL)
668
14.0k
    {
669
14.0k
        uint8_t value = memory->ReadByte(HL);
670
14.0k
        or8(value);
671
14.0k
    }
672
14.0k
        return 7;
673
1.50M
    case 0xB7: // OR A
674
1.50M
        or8(A);
675
1.50M
        return 4;
676
342k
    case 0xB8: // CP B
677
342k
        cp8(B);
678
342k
        return 4;
679
414
    case 0xB9: // CP C
680
414
        cp8(C);
681
414
        return 4;
682
58
    case 0xBA: // CP D
683
58
        cp8(D);
684
58
        return 4;
685
8.00k
    case 0xBB: // CP E
686
8.00k
        cp8(E);
687
8.00k
        return 4;
688
162
    case 0xBC: // CP H
689
162
        cp8(H);
690
162
        return 4;
691
5.51k
    case 0xBD: // CP L
692
5.51k
        cp8(L);
693
5.51k
        return 4;
694
659
    case 0xBE: // CP (HL)
695
659
    {
696
659
        uint8_t value = memory->ReadByte(HL);
697
659
        cp8(value);
698
659
    }
699
659
        return 7;
700
10
    case 0xBF: // CP A
701
10
        cp8(A);
702
10
        return 4;
703
704
    // RET cc instructions
705
616k
    case 0xC0: // RET NZ
706
616k
        if (!GetFlag(FLAG_Z))
707
82.2k
        {
708
82.2k
            PC = Pop();
709
82.2k
            MEMPTR = PC;
710
82.2k
            return 11;
711
82.2k
        }
712
534k
        return 5;
713
255k
    case 0xC1: // POP BC
714
255k
        BC = Pop();
715
255k
        return 10;
716
47.8k
    case 0xC2: // JP NZ, nn
717
47.8k
    {
718
47.8k
        uint16_t addr = ReadImmediateWord();
719
47.8k
        MEMPTR = addr;
720
47.8k
        if (!GetFlag(FLAG_Z))
721
43.4k
        {
722
43.4k
            PC = addr;
723
43.4k
            return 10;
724
43.4k
        }
725
4.39k
        return 10;
726
47.8k
    }
727
2.07M
    case 0xC3: // JP nn
728
2.07M
    {
729
2.07M
        uint16_t addr = ReadImmediateWord();
730
2.07M
        PC = addr;
731
2.07M
        MEMPTR = addr;
732
2.07M
    }
733
2.07M
        return 10;
734
13.3k
    case 0xC4: // CALL NZ, nn
735
13.3k
    {
736
13.3k
        uint16_t addr = ReadImmediateWord();
737
13.3k
        MEMPTR = addr;
738
13.3k
        if (!GetFlag(FLAG_Z))
739
27
        {
740
27
            Push(PC);
741
27
            PC = addr;
742
27
            return 17;
743
27
        }
744
13.2k
        return 10;
745
13.3k
    }
746
231k
    case 0xC5: // PUSH BC
747
231k
        Push(BC);
748
231k
        return 11;
749
146k
    case 0xC6: // ADD A, n
750
146k
    {
751
146k
        uint8_t value = ReadImmediateByte();
752
146k
        add8(value);
753
146k
    }
754
146k
        return 7;
755
0
    case 0xC7: // RST 00H
756
0
        Push(PC);
757
0
        PC = 0x0000;
758
0
        MEMPTR = 0x0000;
759
0
        return 11;
760
10.1M
    case 0xC8: // RET Z
761
10.1M
        if (GetFlag(FLAG_Z))
762
19.8k
        {
763
19.8k
            PC = Pop();
764
19.8k
            MEMPTR = PC;
765
19.8k
            return 11;
766
19.8k
        }
767
10.1M
        return 5;
768
1.00M
    case 0xC9: // RET
769
1.00M
        PC = Pop();
770
1.00M
        MEMPTR = PC;
771
1.00M
        return 10;
772
40.1k
    case 0xCA: // JP Z, nn
773
40.1k
    {
774
40.1k
        uint16_t addr = ReadImmediateWord();
775
40.1k
        MEMPTR = addr;
776
40.1k
        if (GetFlag(FLAG_Z))
777
29.3k
        {
778
29.3k
            PC = addr;
779
29.3k
            return 10;
780
29.3k
        }
781
10.7k
        return 10;
782
40.1k
    }
783
0
    case 0xCB: // PREFIX CB
784
        // This should never be reached as it's handled in ExecuteOneInstruction
785
0
        return 0;
786
4.89k
    case 0xCC: // CALL Z, nn
787
4.89k
    {
788
4.89k
        uint16_t addr = ReadImmediateWord();
789
4.89k
        MEMPTR = addr;
790
4.89k
        if (GetFlag(FLAG_Z))
791
3.81k
        {
792
3.81k
            Push(PC);
793
3.81k
            PC = addr;
794
3.81k
            return 17;
795
3.81k
        }
796
1.07k
        return 10;
797
4.89k
    }
798
1.10M
    case 0xCD: // CALL nn
799
1.10M
    {
800
1.10M
        uint16_t addr = ReadImmediateWord();
801
1.10M
        Push(PC);
802
1.10M
        PC = addr;
803
1.10M
        MEMPTR = addr;
804
1.10M
    }
805
1.10M
        return 17;
806
36
    case 0xCE: // ADC A, n
807
36
    {
808
36
        uint8_t value = ReadImmediateByte();
809
36
        adc8(value);
810
36
    }
811
36
        return 7;
812
0
    case 0xCF: // RST 08H
813
0
        Push(PC);
814
0
        PC = 0x0008;
815
0
        MEMPTR = 0x0008;
816
0
        return 11;
817
10.6M
    case 0xD0: // RET NC
818
10.6M
        if (!GetFlag(FLAG_C))
819
843
        {
820
843
            PC = Pop();
821
843
            MEMPTR = PC;
822
843
            return 11;
823
843
        }
824
10.6M
        return 5;
825
1.21M
    case 0xD1: // POP DE
826
1.21M
        DE = Pop();
827
1.21M
        return 10;
828
358k
    case 0xD2: // JP NC, nn
829
358k
    {
830
358k
        uint16_t addr = ReadImmediateWord();
831
358k
        MEMPTR = addr;
832
358k
        if (!GetFlag(FLAG_C))
833
307k
        {
834
307k
            PC = addr;
835
307k
            return 10;
836
307k
        }
837
50.9k
        return 10;
838
358k
    }
839
717k
    case 0xD3: // OUT (n), A
840
717k
    {
841
717k
        uint8_t n = ReadImmediateByte();
842
717k
        uint16_t portw = uint16_t(n) | (uint16_t(A) << 8);
843
717k
        port->Write(portw, A);
844
717k
        MEMPTR = (uint16_t(A) << 8) | uint16_t((n + 1) & 0xFF);
845
717k
    }
846
717k
        return 11;
847
4.92k
    case 0xD4: // CALL NC, nn
848
4.92k
    {
849
4.92k
        uint16_t addr = ReadImmediateWord();
850
4.92k
        MEMPTR = addr;
851
4.92k
        if (!GetFlag(FLAG_C))
852
45
        {
853
45
            Push(PC);
854
45
            PC = addr;
855
45
            return 17;
856
45
        }
857
4.88k
        return 10;
858
4.92k
    }
859
177k
    case 0xD5: // PUSH DE
860
177k
        Push(DE);
861
177k
        return 11;
862
95.4k
    case 0xD6: // SUB n
863
95.4k
    {
864
95.4k
        uint8_t value = ReadImmediateByte();
865
95.4k
        sub8(value);
866
95.4k
    }
867
95.4k
        return 7;
868
306
    case 0xD7: // RST 10H
869
306
        Push(PC);
870
306
        PC = 0x0010;
871
306
        MEMPTR = 0x0010;
872
306
        return 11;
873
3.60k
    case 0xD8: // RET C
874
3.60k
        if (GetFlag(FLAG_C))
875
3.21k
        {
876
3.21k
            PC = Pop();
877
3.21k
            MEMPTR = PC;
878
3.21k
            return 11;
879
3.21k
        }
880
386
        return 5;
881
1.84M
    case 0xD9: // EXX
882
1.84M
    {
883
1.84M
        uint16_t tempBC = BC;
884
1.84M
        uint16_t tempDE = DE;
885
1.84M
        uint16_t tempHL = HL;
886
1.84M
        BC = BC_;
887
1.84M
        DE = DE_;
888
1.84M
        HL = HL_;
889
1.84M
        BC_ = tempBC;
890
1.84M
        DE_ = tempDE;
891
1.84M
        HL_ = tempHL;
892
1.84M
    }
893
1.84M
        return 4;
894
2.92k
    case 0xDA: // JP C, nn
895
2.92k
    {
896
2.92k
        uint16_t addr = ReadImmediateWord();
897
2.92k
        MEMPTR = addr;
898
2.92k
        if (GetFlag(FLAG_C))
899
1.79k
        {
900
1.79k
            PC = addr;
901
1.79k
            return 10;
902
1.79k
        }
903
1.12k
        return 10;
904
2.92k
    }
905
9.95M
    case 0xDB: // IN A, (n)
906
9.95M
    {
907
9.95M
        uint8_t n = ReadImmediateByte();
908
9.95M
        uint16_t portr = uint16_t(n) | (uint16_t(A) << 8);
909
9.95M
        A = port->Read(portr);
910
9.95M
        MEMPTR = (uint16_t(A) << 8) | uint16_t((n + 1) & 0xFF);
911
9.95M
    }
912
9.95M
        return 11;
913
3.02k
    case 0xDC: // CALL C, nn
914
3.02k
    {
915
3.02k
        uint16_t addr = ReadImmediateWord();
916
3.02k
        MEMPTR = addr;
917
3.02k
        if (GetFlag(FLAG_C))
918
2.67k
        {
919
2.67k
            Push(PC);
920
2.67k
            PC = addr;
921
2.67k
            return 17;
922
2.67k
        }
923
346
        return 10;
924
3.02k
    }
925
0
    case 0xDD: // PREFIX DD
926
        // This should never be reached as it's handled in ExecuteOneInstruction
927
0
        return 0;
928
0
    case 0xDE: // SBC A, n
929
0
    {
930
0
        uint8_t value = ReadImmediateByte();
931
0
        sbc8(value);
932
0
    }
933
0
        return 7;
934
54
    case 0xDF: // RST 18H
935
54
        Push(PC);
936
54
        PC = 0x0018;
937
54
        MEMPTR = 0x0018;
938
54
        return 11;
939
0
    case 0xE0: // RET PO
940
0
        if (!GetFlag(FLAG_PV))
941
0
        {
942
0
            PC = Pop();
943
0
            MEMPTR = PC;
944
0
            return 11;
945
0
        }
946
0
        return 5;
947
318k
    case 0xE1: // POP HL
948
318k
        HL = Pop();
949
318k
        return 10;
950
0
    case 0xE2: // JP PO, nn
951
0
    {
952
0
        uint16_t addr = ReadImmediateWord();
953
0
        MEMPTR = addr;
954
0
        if (!GetFlag(FLAG_PV))
955
0
        {
956
0
            PC = addr;
957
0
            return 10;
958
0
        }
959
0
        return 10;
960
0
    }
961
2.71k
    case 0xE3: // EX (SP), HL
962
2.71k
    {
963
2.71k
        uint16_t temp = (uint16_t(memory->ReadByte(SP + 1)) << 8) | uint16_t(memory->ReadByte(SP));
964
2.71k
        memory->WriteByte(SP, uint8_t(HL & 0xFF));
965
2.71k
        memory->WriteByte(SP + 1, uint8_t((HL >> 8) & 0xFF));
966
2.71k
        HL = temp;
967
2.71k
        MEMPTR = temp;
968
2.71k
    }
969
2.71k
        return 19;
970
0
    case 0xE4: // CALL PO, nn
971
0
    {
972
0
        uint16_t addr = ReadImmediateWord();
973
0
        MEMPTR = addr;
974
0
        if (!GetFlag(FLAG_PV))
975
0
        {
976
0
            Push(PC);
977
0
            PC = addr;
978
0
            return 17;
979
0
        }
980
0
        return 10;
981
0
    }
982
376k
    case 0xE5: // PUSH HL
983
376k
        Push(HL);
984
376k
        return 11;
985
10.3M
    case 0xE6: // AND n
986
10.3M
    {
987
10.3M
        uint8_t value = ReadImmediateByte();
988
10.3M
        and8(value);
989
10.3M
    }
990
10.3M
        return 7;
991
20
    case 0xE7: // RST 20H
992
20
        Push(PC);
993
20
        PC = 0x0020;
994
20
        MEMPTR = 0x0020;
995
20
        return 11;
996
0
    case 0xE8: // RET PE
997
0
        if (GetFlag(FLAG_PV))
998
0
        {
999
0
            PC = Pop();
1000
0
            MEMPTR = PC;
1001
0
            return 11;
1002
0
        }
1003
0
        return 5;
1004
4.62k
    case 0xE9: // JP (HL)
1005
4.62k
        PC = HL;
1006
4.62k
        return 4;
1007
17.5k
    case 0xEA: // JP PE, nn
1008
17.5k
    {
1009
17.5k
        uint16_t addr = ReadImmediateWord();
1010
17.5k
        MEMPTR = addr;
1011
17.5k
        if (GetFlag(FLAG_PV))
1012
13.5k
        {
1013
13.5k
            PC = addr;
1014
13.5k
            return 10;
1015
13.5k
        }
1016
4.01k
        return 10;
1017
17.5k
    }
1018
97.8k
    case 0xEB: // EX DE, HL
1019
97.8k
    {
1020
97.8k
        uint16_t temp = DE;
1021
97.8k
        DE = HL;
1022
97.8k
        HL = temp;
1023
97.8k
    }
1024
97.8k
        return 4;
1025
0
    case 0xEC: // CALL PE, nn
1026
0
    {
1027
0
        uint16_t addr = ReadImmediateWord();
1028
0
        MEMPTR = addr;
1029
0
        if (GetFlag(FLAG_PV))
1030
0
        {
1031
0
            Push(PC);
1032
0
            PC = addr;
1033
0
            return 17;
1034
0
        }
1035
0
        return 10;
1036
0
    }
1037
0
    case 0xED: // PREFIX ED
1038
        // This should never be reached as it's handled in ExecuteOneInstruction
1039
0
        return 0;
1040
2.84k
    case 0xEE: // XOR n
1041
2.84k
    {
1042
2.84k
        uint8_t value = ReadImmediateByte();
1043
2.84k
        xor8(value);
1044
2.84k
    }
1045
2.84k
        return 7;
1046
635
    case 0xEF: // RST 28H
1047
635
        Push(PC);
1048
635
        PC = 0x0028;
1049
635
        MEMPTR = 0x0028;
1050
635
        return 11;
1051
0
    case 0xF0: // RET P
1052
0
        if (!GetFlag(FLAG_S))
1053
0
        {
1054
0
            PC = Pop();
1055
0
            MEMPTR = PC;
1056
0
            return 11;
1057
0
        }
1058
0
        return 5;
1059
197k
    case 0xF1: // POP AF
1060
197k
        AF = Pop();
1061
197k
        return 10;
1062
11.8k
    case 0xF2: // JP P, nn
1063
11.8k
    {
1064
11.8k
        uint16_t addr = ReadImmediateWord();
1065
11.8k
        MEMPTR = addr;
1066
11.8k
        if (!GetFlag(FLAG_S))
1067
11.6k
        {
1068
11.6k
            PC = addr;
1069
11.6k
            return 10;
1070
11.6k
        }
1071
181
        return 10;
1072
11.8k
    }
1073
1.34k
    case 0xF3: // DI
1074
1.34k
        IFF1 = false;
1075
1.34k
        IFF2 = false;
1076
        // printf("DI: %x\n",PC);
1077
1.34k
        return 4;
1078
0
    case 0xF4: // CALL P, nn
1079
0
    {
1080
0
        uint16_t addr = ReadImmediateWord();
1081
0
        MEMPTR = addr;
1082
0
        if (!GetFlag(FLAG_S))
1083
0
        {
1084
0
            Push(PC);
1085
0
            PC = addr;
1086
0
            return 17;
1087
0
        }
1088
0
        return 10;
1089
0
    }
1090
196k
    case 0xF5: // PUSH AF
1091
196k
        Push(AF);
1092
196k
        return 11;
1093
51.7k
    case 0xF6: // OR n
1094
51.7k
    {
1095
51.7k
        uint8_t value = ReadImmediateByte();
1096
51.7k
        or8(value);
1097
51.7k
    }
1098
51.7k
        return 7;
1099
0
    case 0xF7: // RST 30H
1100
0
        Push(PC);
1101
0
        PC = 0x0030;
1102
0
        MEMPTR = 0x0030;
1103
0
        return 11;
1104
0
    case 0xF8: // RET M
1105
0
        if (GetFlag(FLAG_S))
1106
0
        {
1107
0
            PC = Pop();
1108
0
            MEMPTR = PC;
1109
0
            return 11;
1110
0
        }
1111
0
        return 5;
1112
48.8k
    case 0xF9: // LD SP, HL
1113
48.8k
        SP = HL;
1114
48.8k
        return 6;
1115
682
    case 0xFA: // JP M, nn
1116
682
    {
1117
682
        uint16_t addr = ReadImmediateWord();
1118
682
        MEMPTR = addr;
1119
682
        if (GetFlag(FLAG_S))
1120
189
        {
1121
189
            PC = addr;
1122
189
            return 10;
1123
189
        }
1124
493
        return 10;
1125
682
    }
1126
1.40k
    case 0xFB: // EI
1127
1.40k
        IFF1 = true;
1128
1.40k
        IFF2 = true;
1129
        // printf("EI: %x\n",PC);
1130
1.40k
        return 4;
1131
0
    case 0xFC: // CALL M, nn
1132
0
    {
1133
0
        uint16_t addr = ReadImmediateWord();
1134
0
        MEMPTR = addr;
1135
0
        if (GetFlag(FLAG_S))
1136
0
        {
1137
0
            Push(PC);
1138
0
            PC = addr;
1139
0
            return 17;
1140
0
        }
1141
0
        return 10;
1142
0
    }
1143
0
    case 0xFD: // PREFIX FD
1144
        // This should never be reached as it's handled in ExecuteOneInstruction
1145
0
        return 0;
1146
1.20M
    case 0xFE: // CP n
1147
1.20M
    {
1148
1.20M
        uint8_t value = ReadImmediateByte();
1149
1.20M
        cp8(value);
1150
1.20M
    }
1151
1.20M
        return 7;
1152
0
    case 0xFF: // RST 38H
1153
0
        Push(PC);
1154
0
        PC = 0x0038;
1155
0
        MEMPTR = 0x0038;
1156
0
        return 11;
1157
0
    default:
1158
        // This should never happen in a correct implementation
1159
0
        return 4;
1160
163M
    }
1161
1162
    // Default return (should not reach here)
1163
0
    return 4;
1164
163M
}
/opt/homebrew/include/SDL3/SDL_audio.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryAudio
24
 *
25
 * Audio functionality for the SDL library.
26
 *
27
 * All audio in SDL3 revolves around SDL_AudioStream. Whether you want to play
28
 * or record audio, convert it, stream it, buffer it, or mix it, you're going
29
 * to be passing it through an audio stream.
30
 *
31
 * Audio streams are quite flexible; they can accept any amount of data at a
32
 * time, in any supported format, and output it as needed in any other format,
33
 * even if the data format changes on either side halfway through.
34
 *
35
 * An app opens an audio device and binds any number of audio streams to it,
36
 * feeding more data to the streams as available. When the device needs more
37
 * data, it will pull it from all bound streams and mix them together for
38
 * playback.
39
 *
40
 * Audio streams can also use an app-provided callback to supply data
41
 * on-demand, which maps pretty closely to the SDL2 audio model.
42
 *
43
 * SDL also provides a simple .WAV loader in SDL_LoadWAV (and SDL_LoadWAV_IO
44
 * if you aren't reading from a file) as a basic means to load sound data into
45
 * your program.
46
 *
47
 * ## Logical audio devices
48
 *
49
 * In SDL3, opening a physical device (like a SoundBlaster 16 Pro) gives you a
50
 * logical device ID that you can bind audio streams to. In almost all cases,
51
 * logical devices can be used anywhere in the API that a physical device is
52
 * normally used. However, since each device opening generates a new logical
53
 * device, different parts of the program (say, a VoIP library, or
54
 * text-to-speech framework, or maybe some other sort of mixer on top of SDL)
55
 * can have their own device opens that do not interfere with each other; each
56
 * logical device will mix its separate audio down to a single buffer, fed to
57
 * the physical device, behind the scenes. As many logical devices as you like
58
 * can come and go; SDL will only have to open the physical device at the OS
59
 * level once, and will manage all the logical devices on top of it
60
 * internally.
61
 *
62
 * One other benefit of logical devices: if you don't open a specific physical
63
 * device, instead opting for the default, SDL can automatically migrate those
64
 * logical devices to different hardware as circumstances change: a user
65
 * plugged in headphones? The system default changed? SDL can transparently
66
 * migrate the logical devices to the correct physical device seamlessly and
67
 * keep playing; the app doesn't even have to know it happened if it doesn't
68
 * want to.
69
 *
70
 * ## Simplified audio
71
 *
72
 * As a simplified model for when a single source of audio is all that's
73
 * needed, an app can use SDL_OpenAudioDeviceStream, which is a single
74
 * function to open an audio device, create an audio stream, bind that stream
75
 * to the newly-opened device, and (optionally) provide a callback for
76
 * obtaining audio data. When using this function, the primary interface is
77
 * the SDL_AudioStream and the device handle is mostly hidden away; destroying
78
 * a stream created through this function will also close the device, stream
79
 * bindings cannot be changed, etc. One other quirk of this is that the device
80
 * is started in a _paused_ state and must be explicitly resumed; this is
81
 * partially to offer a clean migration for SDL2 apps and partially because
82
 * the app might have to do more setup before playback begins; in the
83
 * non-simplified form, nothing will play until a stream is bound to a device,
84
 * so they start _unpaused_.
85
 *
86
 * ## Channel layouts
87
 *
88
 * Audio data passing through SDL is uncompressed PCM data, interleaved. One
89
 * can provide their own decompression through an MP3, etc, decoder, but SDL
90
 * does not provide this directly. Each interleaved channel of data is meant
91
 * to be in a specific order.
92
 *
93
 * Abbreviations:
94
 *
95
 * - FRONT = single mono speaker
96
 * - FL = front left speaker
97
 * - FR = front right speaker
98
 * - FC = front center speaker
99
 * - BL = back left speaker
100
 * - BR = back right speaker
101
 * - SR = surround right speaker
102
 * - SL = surround left speaker
103
 * - BC = back center speaker
104
 * - LFE = low-frequency speaker
105
 *
106
 * These are listed in the order they are laid out in memory, so "FL, FR"
107
 * means "the front left speaker is laid out in memory first, then the front
108
 * right, then it repeats for the next audio frame".
109
 *
110
 * - 1 channel (mono) layout: FRONT
111
 * - 2 channels (stereo) layout: FL, FR
112
 * - 3 channels (2.1) layout: FL, FR, LFE
113
 * - 4 channels (quad) layout: FL, FR, BL, BR
114
 * - 5 channels (4.1) layout: FL, FR, LFE, BL, BR
115
 * - 6 channels (5.1) layout: FL, FR, FC, LFE, BL, BR (last two can also be
116
 *   SL, SR)
117
 * - 7 channels (6.1) layout: FL, FR, FC, LFE, BC, SL, SR
118
 * - 8 channels (7.1) layout: FL, FR, FC, LFE, BL, BR, SL, SR
119
 *
120
 * This is the same order as DirectSound expects, but applied to all
121
 * platforms; SDL will swizzle the channels as necessary if a platform expects
122
 * something different.
123
 *
124
 * SDL_AudioStream can also be provided channel maps to change this ordering
125
 * to whatever is necessary, in other audio processing scenarios.
126
 */
127
128
#ifndef SDL_audio_h_
129
#define SDL_audio_h_
130
131
#include <SDL3/SDL_stdinc.h>
132
#include <SDL3/SDL_endian.h>
133
#include <SDL3/SDL_error.h>
134
#include <SDL3/SDL_mutex.h>
135
#include <SDL3/SDL_properties.h>
136
#include <SDL3/SDL_iostream.h>
137
138
#include <SDL3/SDL_begin_code.h>
139
/* Set up for C function definitions, even when using C++ */
140
#ifdef __cplusplus
141
extern "C" {
142
#endif
143
144
/**
145
 * Mask of bits in an SDL_AudioFormat that contains the format bit size.
146
 *
147
 * Generally one should use SDL_AUDIO_BITSIZE instead of this macro directly.
148
 *
149
 * \since This macro is available since SDL 3.2.0.
150
 */
151
#define SDL_AUDIO_MASK_BITSIZE       (0xFFu)
152
153
/**
154
 * Mask of bits in an SDL_AudioFormat that contain the floating point flag.
155
 *
156
 * Generally one should use SDL_AUDIO_ISFLOAT instead of this macro directly.
157
 *
158
 * \since This macro is available since SDL 3.2.0.
159
 */
160
#define SDL_AUDIO_MASK_FLOAT         (1u<<8)
161
162
/**
163
 * Mask of bits in an SDL_AudioFormat that contain the bigendian flag.
164
 *
165
 * Generally one should use SDL_AUDIO_ISBIGENDIAN or SDL_AUDIO_ISLITTLEENDIAN
166
 * instead of this macro directly.
167
 *
168
 * \since This macro is available since SDL 3.2.0.
169
 */
170
#define SDL_AUDIO_MASK_BIG_ENDIAN    (1u<<12)
171
172
/**
173
 * Mask of bits in an SDL_AudioFormat that contain the signed data flag.
174
 *
175
 * Generally one should use SDL_AUDIO_ISSIGNED instead of this macro directly.
176
 *
177
 * \since This macro is available since SDL 3.2.0.
178
 */
179
#define SDL_AUDIO_MASK_SIGNED        (1u<<15)
180
181
/**
182
 * Define an SDL_AudioFormat value.
183
 *
184
 * SDL does not support custom audio formats, so this macro is not of much use
185
 * externally, but it can be illustrative as to what the various bits of an
186
 * SDL_AudioFormat mean.
187
 *
188
 * For example, SDL_AUDIO_S32LE looks like this:
189
 *
190
 * ```c
191
 * SDL_DEFINE_AUDIO_FORMAT(1, 0, 0, 32)
192
 * ```
193
 *
194
 * \param signed 1 for signed data, 0 for unsigned data.
195
 * \param bigendian 1 for bigendian data, 0 for littleendian data.
196
 * \param flt 1 for floating point data, 0 for integer data.
197
 * \param size number of bits per sample.
198
 * \returns a format value in the style of SDL_AudioFormat.
199
 *
200
 * \threadsafety It is safe to call this macro from any thread.
201
 *
202
 * \since This macro is available since SDL 3.2.0.
203
 */
204
#define SDL_DEFINE_AUDIO_FORMAT(signed, bigendian, flt, size) \
205
    (((Uint16)(signed) << 15) | ((Uint16)(bigendian) << 12) | ((Uint16)(flt) << 8) | ((size) & SDL_AUDIO_MASK_BITSIZE))
206
207
/**
208
 * Audio format.
209
 *
210
 * \since This enum is available since SDL 3.2.0.
211
 *
212
 * \sa SDL_AUDIO_BITSIZE
213
 * \sa SDL_AUDIO_BYTESIZE
214
 * \sa SDL_AUDIO_ISINT
215
 * \sa SDL_AUDIO_ISFLOAT
216
 * \sa SDL_AUDIO_ISBIGENDIAN
217
 * \sa SDL_AUDIO_ISLITTLEENDIAN
218
 * \sa SDL_AUDIO_ISSIGNED
219
 * \sa SDL_AUDIO_ISUNSIGNED
220
 */
221
typedef enum SDL_AudioFormat
222
{
223
    SDL_AUDIO_UNKNOWN   = 0x0000u,  /**< Unspecified audio format */
224
    SDL_AUDIO_U8        = 0x0008u,  /**< Unsigned 8-bit samples */
225
        /* SDL_DEFINE_AUDIO_FORMAT(0, 0, 0, 8), */
226
    SDL_AUDIO_S8        = 0x8008u,  /**< Signed 8-bit samples */
227
        /* SDL_DEFINE_AUDIO_FORMAT(1, 0, 0, 8), */
228
    SDL_AUDIO_S16LE     = 0x8010u,  /**< Signed 16-bit samples */
229
        /* SDL_DEFINE_AUDIO_FORMAT(1, 0, 0, 16), */
230
    SDL_AUDIO_S16BE     = 0x9010u,  /**< As above, but big-endian byte order */
231
        /* SDL_DEFINE_AUDIO_FORMAT(1, 1, 0, 16), */
232
    SDL_AUDIO_S32LE     = 0x8020u,  /**< 32-bit integer samples */
233
        /* SDL_DEFINE_AUDIO_FORMAT(1, 0, 0, 32), */
234
    SDL_AUDIO_S32BE     = 0x9020u,  /**< As above, but big-endian byte order */
235
        /* SDL_DEFINE_AUDIO_FORMAT(1, 1, 0, 32), */
236
    SDL_AUDIO_F32LE     = 0x8120u,  /**< 32-bit floating point samples */
237
        /* SDL_DEFINE_AUDIO_FORMAT(1, 0, 1, 32), */
238
    SDL_AUDIO_F32BE     = 0x9120u,  /**< As above, but big-endian byte order */
239
        /* SDL_DEFINE_AUDIO_FORMAT(1, 1, 1, 32), */
240
241
    /* These represent the current system's byteorder. */
242
    #if SDL_BYTEORDER == SDL_LIL_ENDIAN
243
    SDL_AUDIO_S16 = SDL_AUDIO_S16LE,
244
    SDL_AUDIO_S32 = SDL_AUDIO_S32LE,
245
    SDL_AUDIO_F32 = SDL_AUDIO_F32LE
246
    #else
247
    SDL_AUDIO_S16 = SDL_AUDIO_S16BE,
248
    SDL_AUDIO_S32 = SDL_AUDIO_S32BE,
249
    SDL_AUDIO_F32 = SDL_AUDIO_F32BE
250
    #endif
251
} SDL_AudioFormat;
252
253
254
/**
255
 * Retrieve the size, in bits, from an SDL_AudioFormat.
256
 *
257
 * For example, `SDL_AUDIO_BITSIZE(SDL_AUDIO_S16)` returns 16.
258
 *
259
 * \param x an SDL_AudioFormat value.
260
 * \returns data size in bits.
261
 *
262
 * \threadsafety It is safe to call this macro from any thread.
263
 *
264
 * \since This macro is available since SDL 3.2.0.
265
 */
266
#define SDL_AUDIO_BITSIZE(x)         ((x) & SDL_AUDIO_MASK_BITSIZE)
267
268
/**
269
 * Retrieve the size, in bytes, from an SDL_AudioFormat.
270
 *
271
 * For example, `SDL_AUDIO_BYTESIZE(SDL_AUDIO_S16)` returns 2.
272
 *
273
 * \param x an SDL_AudioFormat value.
274
 * \returns data size in bytes.
275
 *
276
 * \threadsafety It is safe to call this macro from any thread.
277
 *
278
 * \since This macro is available since SDL 3.2.0.
279
 */
280
#define SDL_AUDIO_BYTESIZE(x)        (SDL_AUDIO_BITSIZE(x) / 8)
281
282
/**
283
 * Determine if an SDL_AudioFormat represents floating point data.
284
 *
285
 * For example, `SDL_AUDIO_ISFLOAT(SDL_AUDIO_S16)` returns 0.
286
 *
287
 * \param x an SDL_AudioFormat value.
288
 * \returns non-zero if format is floating point, zero otherwise.
289
 *
290
 * \threadsafety It is safe to call this macro from any thread.
291
 *
292
 * \since This macro is available since SDL 3.2.0.
293
 */
294
#define SDL_AUDIO_ISFLOAT(x)         ((x) & SDL_AUDIO_MASK_FLOAT)
295
296
/**
297
 * Determine if an SDL_AudioFormat represents bigendian data.
298
 *
299
 * For example, `SDL_AUDIO_ISBIGENDIAN(SDL_AUDIO_S16LE)` returns 0.
300
 *
301
 * \param x an SDL_AudioFormat value.
302
 * \returns non-zero if format is bigendian, zero otherwise.
303
 *
304
 * \threadsafety It is safe to call this macro from any thread.
305
 *
306
 * \since This macro is available since SDL 3.2.0.
307
 */
308
#define SDL_AUDIO_ISBIGENDIAN(x)     ((x) & SDL_AUDIO_MASK_BIG_ENDIAN)
309
310
/**
311
 * Determine if an SDL_AudioFormat represents littleendian data.
312
 *
313
 * For example, `SDL_AUDIO_ISLITTLEENDIAN(SDL_AUDIO_S16BE)` returns 0.
314
 *
315
 * \param x an SDL_AudioFormat value.
316
 * \returns non-zero if format is littleendian, zero otherwise.
317
 *
318
 * \threadsafety It is safe to call this macro from any thread.
319
 *
320
 * \since This macro is available since SDL 3.2.0.
321
 */
322
#define SDL_AUDIO_ISLITTLEENDIAN(x)  (!SDL_AUDIO_ISBIGENDIAN(x))
323
324
/**
325
 * Determine if an SDL_AudioFormat represents signed data.
326
 *
327
 * For example, `SDL_AUDIO_ISSIGNED(SDL_AUDIO_U8)` returns 0.
328
 *
329
 * \param x an SDL_AudioFormat value.
330
 * \returns non-zero if format is signed, zero otherwise.
331
 *
332
 * \threadsafety It is safe to call this macro from any thread.
333
 *
334
 * \since This macro is available since SDL 3.2.0.
335
 */
336
#define SDL_AUDIO_ISSIGNED(x)        ((x) & SDL_AUDIO_MASK_SIGNED)
337
338
/**
339
 * Determine if an SDL_AudioFormat represents integer data.
340
 *
341
 * For example, `SDL_AUDIO_ISINT(SDL_AUDIO_F32)` returns 0.
342
 *
343
 * \param x an SDL_AudioFormat value.
344
 * \returns non-zero if format is integer, zero otherwise.
345
 *
346
 * \threadsafety It is safe to call this macro from any thread.
347
 *
348
 * \since This macro is available since SDL 3.2.0.
349
 */
350
#define SDL_AUDIO_ISINT(x)           (!SDL_AUDIO_ISFLOAT(x))
351
352
/**
353
 * Determine if an SDL_AudioFormat represents unsigned data.
354
 *
355
 * For example, `SDL_AUDIO_ISUNSIGNED(SDL_AUDIO_S16)` returns 0.
356
 *
357
 * \param x an SDL_AudioFormat value.
358
 * \returns non-zero if format is unsigned, zero otherwise.
359
 *
360
 * \threadsafety It is safe to call this macro from any thread.
361
 *
362
 * \since This macro is available since SDL 3.2.0.
363
 */
364
#define SDL_AUDIO_ISUNSIGNED(x)      (!SDL_AUDIO_ISSIGNED(x))
365
366
367
/**
368
 * SDL Audio Device instance IDs.
369
 *
370
 * Zero is used to signify an invalid/null device.
371
 *
372
 * \since This datatype is available since SDL 3.2.0.
373
 */
374
typedef Uint32 SDL_AudioDeviceID;
375
376
/**
377
 * A value used to request a default playback audio device.
378
 *
379
 * Several functions that require an SDL_AudioDeviceID will accept this value
380
 * to signify the app just wants the system to choose a default device instead
381
 * of the app providing a specific one.
382
 *
383
 * \since This macro is available since SDL 3.2.0.
384
 */
385
2
#define SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK ((SDL_AudioDeviceID) 0xFFFFFFFFu)
386
387
/**
388
 * A value used to request a default recording audio device.
389
 *
390
 * Several functions that require an SDL_AudioDeviceID will accept this value
391
 * to signify the app just wants the system to choose a default device instead
392
 * of the app providing a specific one.
393
 *
394
 * \since This macro is available since SDL 3.2.0.
395
 */
396
#define SDL_AUDIO_DEVICE_DEFAULT_RECORDING ((SDL_AudioDeviceID) 0xFFFFFFFEu)
397
398
/**
399
 * Format specifier for audio data.
400
 *
401
 * \since This struct is available since SDL 3.2.0.
402
 *
403
 * \sa SDL_AudioFormat
404
 */
405
typedef struct SDL_AudioSpec
406
{
407
    SDL_AudioFormat format;     /**< Audio data format */
408
    int channels;               /**< Number of channels: 1 mono, 2 stereo, etc */
409
    int freq;                   /**< sample rate: sample frames per second */
410
} SDL_AudioSpec;
411
412
/**
413
 * Calculate the size of each audio frame (in bytes) from an SDL_AudioSpec.
414
 *
415
 * This reports on the size of an audio sample frame: stereo Sint16 data (2
416
 * channels of 2 bytes each) would be 4 bytes per frame, for example.
417
 *
418
 * \param x an SDL_AudioSpec to query.
419
 * \returns the number of bytes used per sample frame.
420
 *
421
 * \threadsafety It is safe to call this macro from any thread.
422
 *
423
 * \since This macro is available since SDL 3.2.0.
424
 */
425
#define SDL_AUDIO_FRAMESIZE(x) (SDL_AUDIO_BYTESIZE((x).format) * (x).channels)
426
427
/**
428
 * The opaque handle that represents an audio stream.
429
 *
430
 * SDL_AudioStream is an audio conversion interface.
431
 *
432
 * - It can handle resampling data in chunks without generating artifacts,
433
 *   when it doesn't have the complete buffer available.
434
 * - It can handle incoming data in any variable size.
435
 * - It can handle input/output format changes on the fly.
436
 * - It can remap audio channels between inputs and outputs.
437
 * - You push data as you have it, and pull it when you need it
438
 * - It can also function as a basic audio data queue even if you just have
439
 *   sound that needs to pass from one place to another.
440
 * - You can hook callbacks up to them when more data is added or requested,
441
 *   to manage data on-the-fly.
442
 *
443
 * Audio streams are the core of the SDL3 audio interface. You create one or
444
 * more of them, bind them to an opened audio device, and feed data to them
445
 * (or for recording, consume data from them).
446
 *
447
 * \since This struct is available since SDL 3.2.0.
448
 *
449
 * \sa SDL_CreateAudioStream
450
 */
451
typedef struct SDL_AudioStream SDL_AudioStream;
452
453
454
/* Function prototypes */
455
456
/**
457
 * Use this function to get the number of built-in audio drivers.
458
 *
459
 * This function returns a hardcoded number. This never returns a negative
460
 * value; if there are no drivers compiled into this build of SDL, this
461
 * function returns zero. The presence of a driver in this list does not mean
462
 * it will function, it just means SDL is capable of interacting with that
463
 * interface. For example, a build of SDL might have esound support, but if
464
 * there's no esound server available, SDL's esound driver would fail if used.
465
 *
466
 * By default, SDL tries all drivers, in its preferred order, until one is
467
 * found to be usable.
468
 *
469
 * \returns the number of built-in audio drivers.
470
 *
471
 * \threadsafety It is safe to call this function from any thread.
472
 *
473
 * \since This function is available since SDL 3.2.0.
474
 *
475
 * \sa SDL_GetAudioDriver
476
 */
477
extern SDL_DECLSPEC int SDLCALL SDL_GetNumAudioDrivers(void);
478
479
/**
480
 * Use this function to get the name of a built in audio driver.
481
 *
482
 * The list of audio drivers is given in the order that they are normally
483
 * initialized by default; the drivers that seem more reasonable to choose
484
 * first (as far as the SDL developers believe) are earlier in the list.
485
 *
486
 * The names of drivers are all simple, low-ASCII identifiers, like "alsa",
487
 * "coreaudio" or "wasapi". These never have Unicode characters, and are not
488
 * meant to be proper names.
489
 *
490
 * \param index the index of the audio driver; the value ranges from 0 to
491
 *              SDL_GetNumAudioDrivers() - 1.
492
 * \returns the name of the audio driver at the requested index, or NULL if an
493
 *          invalid index was specified.
494
 *
495
 * \threadsafety It is safe to call this function from any thread.
496
 *
497
 * \since This function is available since SDL 3.2.0.
498
 *
499
 * \sa SDL_GetNumAudioDrivers
500
 */
501
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAudioDriver(int index);
502
503
/**
504
 * Get the name of the current audio driver.
505
 *
506
 * The names of drivers are all simple, low-ASCII identifiers, like "alsa",
507
 * "coreaudio" or "wasapi". These never have Unicode characters, and are not
508
 * meant to be proper names.
509
 *
510
 * \returns the name of the current audio driver or NULL if no driver has been
511
 *          initialized.
512
 *
513
 * \threadsafety It is safe to call this function from any thread.
514
 *
515
 * \since This function is available since SDL 3.2.0.
516
 */
517
extern SDL_DECLSPEC const char * SDLCALL SDL_GetCurrentAudioDriver(void);
518
519
/**
520
 * Get a list of currently-connected audio playback devices.
521
 *
522
 * This returns of list of available devices that play sound, perhaps to
523
 * speakers or headphones ("playback" devices). If you want devices that
524
 * record audio, like a microphone ("recording" devices), use
525
 * SDL_GetAudioRecordingDevices() instead.
526
 *
527
 * This only returns a list of physical devices; it will not have any device
528
 * IDs returned by SDL_OpenAudioDevice().
529
 *
530
 * If this function returns NULL, to signify an error, `*count` will be set to
531
 * zero.
532
 *
533
 * \param count a pointer filled in with the number of devices returned, may
534
 *              be NULL.
535
 * \returns a 0 terminated array of device instance IDs or NULL on error; call
536
 *          SDL_GetError() for more information. This should be freed with
537
 *          SDL_free() when it is no longer needed.
538
 *
539
 * \threadsafety It is safe to call this function from any thread.
540
 *
541
 * \since This function is available since SDL 3.2.0.
542
 *
543
 * \sa SDL_OpenAudioDevice
544
 * \sa SDL_GetAudioRecordingDevices
545
 */
546
extern SDL_DECLSPEC SDL_AudioDeviceID * SDLCALL SDL_GetAudioPlaybackDevices(int *count);
547
548
/**
549
 * Get a list of currently-connected audio recording devices.
550
 *
551
 * This returns of list of available devices that record audio, like a
552
 * microphone ("recording" devices). If you want devices that play sound,
553
 * perhaps to speakers or headphones ("playback" devices), use
554
 * SDL_GetAudioPlaybackDevices() instead.
555
 *
556
 * This only returns a list of physical devices; it will not have any device
557
 * IDs returned by SDL_OpenAudioDevice().
558
 *
559
 * If this function returns NULL, to signify an error, `*count` will be set to
560
 * zero.
561
 *
562
 * \param count a pointer filled in with the number of devices returned, may
563
 *              be NULL.
564
 * \returns a 0 terminated array of device instance IDs, or NULL on failure;
565
 *          call SDL_GetError() for more information. This should be freed
566
 *          with SDL_free() when it is no longer needed.
567
 *
568
 * \threadsafety It is safe to call this function from any thread.
569
 *
570
 * \since This function is available since SDL 3.2.0.
571
 *
572
 * \sa SDL_OpenAudioDevice
573
 * \sa SDL_GetAudioPlaybackDevices
574
 */
575
extern SDL_DECLSPEC SDL_AudioDeviceID * SDLCALL SDL_GetAudioRecordingDevices(int *count);
576
577
/**
578
 * Get the human-readable name of a specific audio device.
579
 *
580
 * \param devid the instance ID of the device to query.
581
 * \returns the name of the audio device, or NULL on failure; call
582
 *          SDL_GetError() for more information.
583
 *
584
 * \threadsafety It is safe to call this function from any thread.
585
 *
586
 * \since This function is available since SDL 3.2.0.
587
 *
588
 * \sa SDL_GetAudioPlaybackDevices
589
 * \sa SDL_GetAudioRecordingDevices
590
 */
591
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAudioDeviceName(SDL_AudioDeviceID devid);
592
593
/**
594
 * Get the current audio format of a specific audio device.
595
 *
596
 * For an opened device, this will report the format the device is currently
597
 * using. If the device isn't yet opened, this will report the device's
598
 * preferred format (or a reasonable default if this can't be determined).
599
 *
600
 * You may also specify SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK or
601
 * SDL_AUDIO_DEVICE_DEFAULT_RECORDING here, which is useful for getting a
602
 * reasonable recommendation before opening the system-recommended default
603
 * device.
604
 *
605
 * You can also use this to request the current device buffer size. This is
606
 * specified in sample frames and represents the amount of data SDL will feed
607
 * to the physical hardware in each chunk. This can be converted to
608
 * milliseconds of audio with the following equation:
609
 *
610
 * `ms = (int) ((((Sint64) frames) * 1000) / spec.freq);`
611
 *
612
 * Buffer size is only important if you need low-level control over the audio
613
 * playback timing. Most apps do not need this.
614
 *
615
 * \param devid the instance ID of the device to query.
616
 * \param spec on return, will be filled with device details.
617
 * \param sample_frames pointer to store device buffer size, in sample frames.
618
 *                      Can be NULL.
619
 * \returns true on success or false on failure; call SDL_GetError() for more
620
 *          information.
621
 *
622
 * \threadsafety It is safe to call this function from any thread.
623
 *
624
 * \since This function is available since SDL 3.2.0.
625
 */
626
extern SDL_DECLSPEC bool SDLCALL SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames);
627
628
/**
629
 * Get the current channel map of an audio device.
630
 *
631
 * Channel maps are optional; most things do not need them, instead passing
632
 * data in the [order that SDL expects](CategoryAudio#channel-layouts).
633
 *
634
 * Audio devices usually have no remapping applied. This is represented by
635
 * returning NULL, and does not signify an error.
636
 *
637
 * \param devid the instance ID of the device to query.
638
 * \param count On output, set to number of channels in the map. Can be NULL.
639
 * \returns an array of the current channel mapping, with as many elements as
640
 *          the current output spec's channels, or NULL if default. This
641
 *          should be freed with SDL_free() when it is no longer needed.
642
 *
643
 * \threadsafety It is safe to call this function from any thread.
644
 *
645
 * \since This function is available since SDL 3.2.0.
646
 *
647
 * \sa SDL_SetAudioStreamInputChannelMap
648
 */
649
extern SDL_DECLSPEC int * SDLCALL SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count);
650
651
/**
652
 * Open a specific audio device.
653
 *
654
 * You can open both playback and recording devices through this function.
655
 * Playback devices will take data from bound audio streams, mix it, and send
656
 * it to the hardware. Recording devices will feed any bound audio streams
657
 * with a copy of any incoming data.
658
 *
659
 * An opened audio device starts out with no audio streams bound. To start
660
 * audio playing, bind a stream and supply audio data to it. Unlike SDL2,
661
 * there is no audio callback; you only bind audio streams and make sure they
662
 * have data flowing into them (however, you can simulate SDL2's semantics
663
 * fairly closely by using SDL_OpenAudioDeviceStream instead of this
664
 * function).
665
 *
666
 * If you don't care about opening a specific device, pass a `devid` of either
667
 * `SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK` or
668
 * `SDL_AUDIO_DEVICE_DEFAULT_RECORDING`. In this case, SDL will try to pick
669
 * the most reasonable default, and may also switch between physical devices
670
 * seamlessly later, if the most reasonable default changes during the
671
 * lifetime of this opened device (user changed the default in the OS's system
672
 * preferences, the default got unplugged so the system jumped to a new
673
 * default, the user plugged in headphones on a mobile device, etc). Unless
674
 * you have a good reason to choose a specific device, this is probably what
675
 * you want.
676
 *
677
 * You may request a specific format for the audio device, but there is no
678
 * promise the device will honor that request for several reasons. As such,
679
 * it's only meant to be a hint as to what data your app will provide. Audio
680
 * streams will accept data in whatever format you specify and manage
681
 * conversion for you as appropriate. SDL_GetAudioDeviceFormat can tell you
682
 * the preferred format for the device before opening and the actual format
683
 * the device is using after opening.
684
 *
685
 * It's legal to open the same device ID more than once; each successful open
686
 * will generate a new logical SDL_AudioDeviceID that is managed separately
687
 * from others on the same physical device. This allows libraries to open a
688
 * device separately from the main app and bind its own streams without
689
 * conflicting.
690
 *
691
 * It is also legal to open a device ID returned by a previous call to this
692
 * function; doing so just creates another logical device on the same physical
693
 * device. This may be useful for making logical groupings of audio streams.
694
 *
695
 * This function returns the opened device ID on success. This is a new,
696
 * unique SDL_AudioDeviceID that represents a logical device.
697
 *
698
 * Some backends might offer arbitrary devices (for example, a networked audio
699
 * protocol that can connect to an arbitrary server). For these, as a change
700
 * from SDL2, you should open a default device ID and use an SDL hint to
701
 * specify the target if you care, or otherwise let the backend figure out a
702
 * reasonable default. Most backends don't offer anything like this, and often
703
 * this would be an end user setting an environment variable for their custom
704
 * need, and not something an application should specifically manage.
705
 *
706
 * When done with an audio device, possibly at the end of the app's life, one
707
 * should call SDL_CloseAudioDevice() on the returned device id.
708
 *
709
 * \param devid the device instance id to open, or
710
 *              SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK or
711
 *              SDL_AUDIO_DEVICE_DEFAULT_RECORDING for the most reasonable
712
 *              default device.
713
 * \param spec the requested device configuration. Can be NULL to use
714
 *             reasonable defaults.
715
 * \returns the device ID on success or 0 on failure; call SDL_GetError() for
716
 *          more information.
717
 *
718
 * \threadsafety It is safe to call this function from any thread.
719
 *
720
 * \since This function is available since SDL 3.2.0.
721
 *
722
 * \sa SDL_CloseAudioDevice
723
 * \sa SDL_GetAudioDeviceFormat
724
 */
725
extern SDL_DECLSPEC SDL_AudioDeviceID SDLCALL SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec);
726
727
/**
728
 * Determine if an audio device is physical (instead of logical).
729
 *
730
 * An SDL_AudioDeviceID that represents physical hardware is a physical
731
 * device; there is one for each piece of hardware that SDL can see. Logical
732
 * devices are created by calling SDL_OpenAudioDevice or
733
 * SDL_OpenAudioDeviceStream, and while each is associated with a physical
734
 * device, there can be any number of logical devices on one physical device.
735
 *
736
 * For the most part, logical and physical IDs are interchangeable--if you try
737
 * to open a logical device, SDL understands to assign that effort to the
738
 * underlying physical device, etc. However, it might be useful to know if an
739
 * arbitrary device ID is physical or logical. This function reports which.
740
 *
741
 * This function may return either true or false for invalid device IDs.
742
 *
743
 * \param devid the device ID to query.
744
 * \returns true if devid is a physical device, false if it is logical.
745
 *
746
 * \threadsafety It is safe to call this function from any thread.
747
 *
748
 * \since This function is available since SDL 3.2.0.
749
 */
750
extern SDL_DECLSPEC bool SDLCALL SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid);
751
752
/**
753
 * Determine if an audio device is a playback device (instead of recording).
754
 *
755
 * This function may return either true or false for invalid device IDs.
756
 *
757
 * \param devid the device ID to query.
758
 * \returns true if devid is a playback device, false if it is recording.
759
 *
760
 * \threadsafety It is safe to call this function from any thread.
761
 *
762
 * \since This function is available since SDL 3.2.0.
763
 */
764
extern SDL_DECLSPEC bool SDLCALL SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid);
765
766
/**
767
 * Use this function to pause audio playback on a specified device.
768
 *
769
 * This function pauses audio processing for a given device. Any bound audio
770
 * streams will not progress, and no audio will be generated. Pausing one
771
 * device does not prevent other unpaused devices from running.
772
 *
773
 * Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
774
 * has to bind a stream before any audio will flow. Pausing a paused device is
775
 * a legal no-op.
776
 *
777
 * Pausing a device can be useful to halt all audio without unbinding all the
778
 * audio streams. This might be useful while a game is paused, or a level is
779
 * loading, etc.
780
 *
781
 * Physical devices can not be paused or unpaused, only logical devices
782
 * created through SDL_OpenAudioDevice() can be.
783
 *
784
 * \param devid a device opened by SDL_OpenAudioDevice().
785
 * \returns true on success or false on failure; call SDL_GetError() for more
786
 *          information.
787
 *
788
 * \threadsafety It is safe to call this function from any thread.
789
 *
790
 * \since This function is available since SDL 3.2.0.
791
 *
792
 * \sa SDL_ResumeAudioDevice
793
 * \sa SDL_AudioDevicePaused
794
 */
795
extern SDL_DECLSPEC bool SDLCALL SDL_PauseAudioDevice(SDL_AudioDeviceID devid);
796
797
/**
798
 * Use this function to unpause audio playback on a specified device.
799
 *
800
 * This function unpauses audio processing for a given device that has
801
 * previously been paused with SDL_PauseAudioDevice(). Once unpaused, any
802
 * bound audio streams will begin to progress again, and audio can be
803
 * generated.
804
 *
805
 * Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
806
 * has to bind a stream before any audio will flow. Unpausing an unpaused
807
 * device is a legal no-op.
808
 *
809
 * Physical devices can not be paused or unpaused, only logical devices
810
 * created through SDL_OpenAudioDevice() can be.
811
 *
812
 * \param devid a device opened by SDL_OpenAudioDevice().
813
 * \returns true on success or false on failure; call SDL_GetError() for more
814
 *          information.
815
 *
816
 * \threadsafety It is safe to call this function from any thread.
817
 *
818
 * \since This function is available since SDL 3.2.0.
819
 *
820
 * \sa SDL_AudioDevicePaused
821
 * \sa SDL_PauseAudioDevice
822
 */
823
extern SDL_DECLSPEC bool SDLCALL SDL_ResumeAudioDevice(SDL_AudioDeviceID devid);
824
825
/**
826
 * Use this function to query if an audio device is paused.
827
 *
828
 * Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
829
 * has to bind a stream before any audio will flow.
830
 *
831
 * Physical devices can not be paused or unpaused, only logical devices
832
 * created through SDL_OpenAudioDevice() can be. Physical and invalid device
833
 * IDs will report themselves as unpaused here.
834
 *
835
 * \param devid a device opened by SDL_OpenAudioDevice().
836
 * \returns true if device is valid and paused, false otherwise.
837
 *
838
 * \threadsafety It is safe to call this function from any thread.
839
 *
840
 * \since This function is available since SDL 3.2.0.
841
 *
842
 * \sa SDL_PauseAudioDevice
843
 * \sa SDL_ResumeAudioDevice
844
 */
845
extern SDL_DECLSPEC bool SDLCALL SDL_AudioDevicePaused(SDL_AudioDeviceID devid);
846
847
/**
848
 * Get the gain of an audio device.
849
 *
850
 * The gain of a device is its volume; a larger gain means a louder output,
851
 * with a gain of zero being silence.
852
 *
853
 * Audio devices default to a gain of 1.0f (no change in output).
854
 *
855
 * Physical devices may not have their gain changed, only logical devices, and
856
 * this function will always return -1.0f when used on physical devices.
857
 *
858
 * \param devid the audio device to query.
859
 * \returns the gain of the device or -1.0f on failure; call SDL_GetError()
860
 *          for more information.
861
 *
862
 * \threadsafety It is safe to call this function from any thread.
863
 *
864
 * \since This function is available since SDL 3.2.0.
865
 *
866
 * \sa SDL_SetAudioDeviceGain
867
 */
868
extern SDL_DECLSPEC float SDLCALL SDL_GetAudioDeviceGain(SDL_AudioDeviceID devid);
869
870
/**
871
 * Change the gain of an audio device.
872
 *
873
 * The gain of a device is its volume; a larger gain means a louder output,
874
 * with a gain of zero being silence.
875
 *
876
 * Audio devices default to a gain of 1.0f (no change in output).
877
 *
878
 * Physical devices may not have their gain changed, only logical devices, and
879
 * this function will always return false when used on physical devices. While
880
 * it might seem attractive to adjust several logical devices at once in this
881
 * way, it would allow an app or library to interfere with another portion of
882
 * the program's otherwise-isolated devices.
883
 *
884
 * This is applied, along with any per-audiostream gain, during playback to
885
 * the hardware, and can be continuously changed to create various effects. On
886
 * recording devices, this will adjust the gain before passing the data into
887
 * an audiostream; that recording audiostream can then adjust its gain further
888
 * when outputting the data elsewhere, if it likes, but that second gain is
889
 * not applied until the data leaves the audiostream again.
890
 *
891
 * \param devid the audio device on which to change gain.
892
 * \param gain the gain. 1.0f is no change, 0.0f is silence.
893
 * \returns true on success or false on failure; call SDL_GetError() for more
894
 *          information.
895
 *
896
 * \threadsafety It is safe to call this function from any thread, as it holds
897
 *               a stream-specific mutex while running.
898
 *
899
 * \since This function is available since SDL 3.2.0.
900
 *
901
 * \sa SDL_GetAudioDeviceGain
902
 */
903
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain);
904
905
/**
906
 * Close a previously-opened audio device.
907
 *
908
 * The application should close open audio devices once they are no longer
909
 * needed.
910
 *
911
 * This function may block briefly while pending audio data is played by the
912
 * hardware, so that applications don't drop the last buffer of data they
913
 * supplied if terminating immediately afterwards.
914
 *
915
 * \param devid an audio device id previously returned by
916
 *              SDL_OpenAudioDevice().
917
 *
918
 * \threadsafety It is safe to call this function from any thread.
919
 *
920
 * \since This function is available since SDL 3.2.0.
921
 *
922
 * \sa SDL_OpenAudioDevice
923
 */
924
extern SDL_DECLSPEC void SDLCALL SDL_CloseAudioDevice(SDL_AudioDeviceID devid);
925
926
/**
927
 * Bind a list of audio streams to an audio device.
928
 *
929
 * Audio data will flow through any bound streams. For a playback device, data
930
 * for all bound streams will be mixed together and fed to the device. For a
931
 * recording device, a copy of recorded data will be provided to each bound
932
 * stream.
933
 *
934
 * Audio streams can only be bound to an open device. This operation is
935
 * atomic--all streams bound in the same call will start processing at the
936
 * same time, so they can stay in sync. Also: either all streams will be bound
937
 * or none of them will be.
938
 *
939
 * It is an error to bind an already-bound stream; it must be explicitly
940
 * unbound first.
941
 *
942
 * Binding a stream to a device will set its output format for playback
943
 * devices, and its input format for recording devices, so they match the
944
 * device's settings. The caller is welcome to change the other end of the
945
 * stream's format at any time with SDL_SetAudioStreamFormat(). If the other
946
 * end of the stream's format has never been set (the audio stream was created
947
 * with a NULL audio spec), this function will set it to match the device
948
 * end's format.
949
 *
950
 * \param devid an audio device to bind a stream to.
951
 * \param streams an array of audio streams to bind.
952
 * \param num_streams number streams listed in the `streams` array.
953
 * \returns true on success or false on failure; call SDL_GetError() for more
954
 *          information.
955
 *
956
 * \threadsafety It is safe to call this function from any thread.
957
 *
958
 * \since This function is available since SDL 3.2.0.
959
 *
960
 * \sa SDL_BindAudioStreams
961
 * \sa SDL_UnbindAudioStream
962
 * \sa SDL_GetAudioStreamDevice
963
 */
964
extern SDL_DECLSPEC bool SDLCALL SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *streams, int num_streams);
965
966
/**
967
 * Bind a single audio stream to an audio device.
968
 *
969
 * This is a convenience function, equivalent to calling
970
 * `SDL_BindAudioStreams(devid, &stream, 1)`.
971
 *
972
 * \param devid an audio device to bind a stream to.
973
 * \param stream an audio stream to bind to a device.
974
 * \returns true on success or false on failure; call SDL_GetError() for more
975
 *          information.
976
 *
977
 * \threadsafety It is safe to call this function from any thread.
978
 *
979
 * \since This function is available since SDL 3.2.0.
980
 *
981
 * \sa SDL_BindAudioStreams
982
 * \sa SDL_UnbindAudioStream
983
 * \sa SDL_GetAudioStreamDevice
984
 */
985
extern SDL_DECLSPEC bool SDLCALL SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream);
986
987
/**
988
 * Unbind a list of audio streams from their audio devices.
989
 *
990
 * The streams being unbound do not all have to be on the same device. All
991
 * streams on the same device will be unbound atomically (data will stop
992
 * flowing through all unbound streams on the same device at the same time).
993
 *
994
 * Unbinding a stream that isn't bound to a device is a legal no-op.
995
 *
996
 * \param streams an array of audio streams to unbind. Can be NULL or contain
997
 *                NULL.
998
 * \param num_streams number streams listed in the `streams` array.
999
 *
1000
 * \threadsafety It is safe to call this function from any thread.
1001
 *
1002
 * \since This function is available since SDL 3.2.0.
1003
 *
1004
 * \sa SDL_BindAudioStreams
1005
 */
1006
extern SDL_DECLSPEC void SDLCALL SDL_UnbindAudioStreams(SDL_AudioStream * const *streams, int num_streams);
1007
1008
/**
1009
 * Unbind a single audio stream from its audio device.
1010
 *
1011
 * This is a convenience function, equivalent to calling
1012
 * `SDL_UnbindAudioStreams(&stream, 1)`.
1013
 *
1014
 * \param stream an audio stream to unbind from a device. Can be NULL.
1015
 *
1016
 * \threadsafety It is safe to call this function from any thread.
1017
 *
1018
 * \since This function is available since SDL 3.2.0.
1019
 *
1020
 * \sa SDL_BindAudioStream
1021
 */
1022
extern SDL_DECLSPEC void SDLCALL SDL_UnbindAudioStream(SDL_AudioStream *stream);
1023
1024
/**
1025
 * Query an audio stream for its currently-bound device.
1026
 *
1027
 * This reports the logical audio device that an audio stream is currently bound to.
1028
 *
1029
 * If not bound, or invalid, this returns zero, which is not a valid device
1030
 * ID.
1031
 *
1032
 * \param stream the audio stream to query.
1033
 * \returns the bound audio device, or 0 if not bound or invalid.
1034
 *
1035
 * \threadsafety It is safe to call this function from any thread.
1036
 *
1037
 * \since This function is available since SDL 3.2.0.
1038
 *
1039
 * \sa SDL_BindAudioStream
1040
 * \sa SDL_BindAudioStreams
1041
 */
1042
extern SDL_DECLSPEC SDL_AudioDeviceID SDLCALL SDL_GetAudioStreamDevice(SDL_AudioStream *stream);
1043
1044
/**
1045
 * Create a new audio stream.
1046
 *
1047
 * \param src_spec the format details of the input audio.
1048
 * \param dst_spec the format details of the output audio.
1049
 * \returns a new audio stream on success or NULL on failure; call
1050
 *          SDL_GetError() for more information.
1051
 *
1052
 * \threadsafety It is safe to call this function from any thread.
1053
 *
1054
 * \since This function is available since SDL 3.2.0.
1055
 *
1056
 * \sa SDL_PutAudioStreamData
1057
 * \sa SDL_GetAudioStreamData
1058
 * \sa SDL_GetAudioStreamAvailable
1059
 * \sa SDL_FlushAudioStream
1060
 * \sa SDL_ClearAudioStream
1061
 * \sa SDL_SetAudioStreamFormat
1062
 * \sa SDL_DestroyAudioStream
1063
 */
1064
extern SDL_DECLSPEC SDL_AudioStream * SDLCALL SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec);
1065
1066
/**
1067
 * Get the properties associated with an audio stream.
1068
 *
1069
 * \param stream the SDL_AudioStream to query.
1070
 * \returns a valid property ID on success or 0 on failure; call
1071
 *          SDL_GetError() for more information.
1072
 *
1073
 * \threadsafety It is safe to call this function from any thread.
1074
 *
1075
 * \since This function is available since SDL 3.2.0.
1076
 */
1077
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetAudioStreamProperties(SDL_AudioStream *stream);
1078
1079
/**
1080
 * Query the current format of an audio stream.
1081
 *
1082
 * \param stream the SDL_AudioStream to query.
1083
 * \param src_spec where to store the input audio format; ignored if NULL.
1084
 * \param dst_spec where to store the output audio format; ignored if NULL.
1085
 * \returns true on success or false on failure; call SDL_GetError() for more
1086
 *          information.
1087
 *
1088
 * \threadsafety It is safe to call this function from any thread, as it holds
1089
 *               a stream-specific mutex while running.
1090
 *
1091
 * \since This function is available since SDL 3.2.0.
1092
 *
1093
 * \sa SDL_SetAudioStreamFormat
1094
 */
1095
extern SDL_DECLSPEC bool SDLCALL SDL_GetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioSpec *src_spec, SDL_AudioSpec *dst_spec);
1096
1097
/**
1098
 * Change the input and output formats of an audio stream.
1099
 *
1100
 * Future calls to and SDL_GetAudioStreamAvailable and SDL_GetAudioStreamData
1101
 * will reflect the new format, and future calls to SDL_PutAudioStreamData
1102
 * must provide data in the new input formats.
1103
 *
1104
 * Data that was previously queued in the stream will still be operated on in
1105
 * the format that was current when it was added, which is to say you can put
1106
 * the end of a sound file in one format to a stream, change formats for the
1107
 * next sound file, and start putting that new data while the previous sound
1108
 * file is still queued, and everything will still play back correctly.
1109
 *
1110
 * If a stream is bound to a device, then the format of the side of the stream
1111
 * bound to a device cannot be changed (src_spec for recording devices,
1112
 * dst_spec for playback devices). Attempts to make a change to this side will
1113
 * be ignored, but this will not report an error. The other side's format can
1114
 * be changed.
1115
 *
1116
 * \param stream the stream the format is being changed.
1117
 * \param src_spec the new format of the audio input; if NULL, it is not
1118
 *                 changed.
1119
 * \param dst_spec the new format of the audio output; if NULL, it is not
1120
 *                 changed.
1121
 * \returns true on success or false on failure; call SDL_GetError() for more
1122
 *          information.
1123
 *
1124
 * \threadsafety It is safe to call this function from any thread, as it holds
1125
 *               a stream-specific mutex while running.
1126
 *
1127
 * \since This function is available since SDL 3.2.0.
1128
 *
1129
 * \sa SDL_GetAudioStreamFormat
1130
 * \sa SDL_SetAudioStreamFrequencyRatio
1131
 */
1132
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec);
1133
1134
/**
1135
 * Get the frequency ratio of an audio stream.
1136
 *
1137
 * \param stream the SDL_AudioStream to query.
1138
 * \returns the frequency ratio of the stream or 0.0 on failure; call
1139
 *          SDL_GetError() for more information.
1140
 *
1141
 * \threadsafety It is safe to call this function from any thread, as it holds
1142
 *               a stream-specific mutex while running.
1143
 *
1144
 * \since This function is available since SDL 3.2.0.
1145
 *
1146
 * \sa SDL_SetAudioStreamFrequencyRatio
1147
 */
1148
extern SDL_DECLSPEC float SDLCALL SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream);
1149
1150
/**
1151
 * Change the frequency ratio of an audio stream.
1152
 *
1153
 * The frequency ratio is used to adjust the rate at which input data is
1154
 * consumed. Changing this effectively modifies the speed and pitch of the
1155
 * audio. A value greater than 1.0 will play the audio faster, and at a higher
1156
 * pitch. A value less than 1.0 will play the audio slower, and at a lower
1157
 * pitch.
1158
 *
1159
 * This is applied during SDL_GetAudioStreamData, and can be continuously
1160
 * changed to create various effects.
1161
 *
1162
 * \param stream the stream the frequency ratio is being changed.
1163
 * \param ratio the frequency ratio. 1.0 is normal speed. Must be between 0.01
1164
 *              and 100.
1165
 * \returns true on success or false on failure; call SDL_GetError() for more
1166
 *          information.
1167
 *
1168
 * \threadsafety It is safe to call this function from any thread, as it holds
1169
 *               a stream-specific mutex while running.
1170
 *
1171
 * \since This function is available since SDL 3.2.0.
1172
 *
1173
 * \sa SDL_GetAudioStreamFrequencyRatio
1174
 * \sa SDL_SetAudioStreamFormat
1175
 */
1176
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float ratio);
1177
1178
/**
1179
 * Get the gain of an audio stream.
1180
 *
1181
 * The gain of a stream is its volume; a larger gain means a louder output,
1182
 * with a gain of zero being silence.
1183
 *
1184
 * Audio streams default to a gain of 1.0f (no change in output).
1185
 *
1186
 * \param stream the SDL_AudioStream to query.
1187
 * \returns the gain of the stream or -1.0f on failure; call SDL_GetError()
1188
 *          for more information.
1189
 *
1190
 * \threadsafety It is safe to call this function from any thread, as it holds
1191
 *               a stream-specific mutex while running.
1192
 *
1193
 * \since This function is available since SDL 3.2.0.
1194
 *
1195
 * \sa SDL_SetAudioStreamGain
1196
 */
1197
extern SDL_DECLSPEC float SDLCALL SDL_GetAudioStreamGain(SDL_AudioStream *stream);
1198
1199
/**
1200
 * Change the gain of an audio stream.
1201
 *
1202
 * The gain of a stream is its volume; a larger gain means a louder output,
1203
 * with a gain of zero being silence.
1204
 *
1205
 * Audio streams default to a gain of 1.0f (no change in output).
1206
 *
1207
 * This is applied during SDL_GetAudioStreamData, and can be continuously
1208
 * changed to create various effects.
1209
 *
1210
 * \param stream the stream on which the gain is being changed.
1211
 * \param gain the gain. 1.0f is no change, 0.0f is silence.
1212
 * \returns true on success or false on failure; call SDL_GetError() for more
1213
 *          information.
1214
 *
1215
 * \threadsafety It is safe to call this function from any thread, as it holds
1216
 *               a stream-specific mutex while running.
1217
 *
1218
 * \since This function is available since SDL 3.2.0.
1219
 *
1220
 * \sa SDL_GetAudioStreamGain
1221
 */
1222
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain);
1223
1224
/**
1225
 * Get the current input channel map of an audio stream.
1226
 *
1227
 * Channel maps are optional; most things do not need them, instead passing
1228
 * data in the [order that SDL expects](CategoryAudio#channel-layouts).
1229
 *
1230
 * Audio streams default to no remapping applied. This is represented by
1231
 * returning NULL, and does not signify an error.
1232
 *
1233
 * \param stream the SDL_AudioStream to query.
1234
 * \param count On output, set to number of channels in the map. Can be NULL.
1235
 * \returns an array of the current channel mapping, with as many elements as
1236
 *          the current output spec's channels, or NULL if default. This
1237
 *          should be freed with SDL_free() when it is no longer needed.
1238
 *
1239
 * \threadsafety It is safe to call this function from any thread, as it holds
1240
 *               a stream-specific mutex while running.
1241
 *
1242
 * \since This function is available since SDL 3.2.0.
1243
 *
1244
 * \sa SDL_SetAudioStreamInputChannelMap
1245
 */
1246
extern SDL_DECLSPEC int * SDLCALL SDL_GetAudioStreamInputChannelMap(SDL_AudioStream *stream, int *count);
1247
1248
/**
1249
 * Get the current output channel map of an audio stream.
1250
 *
1251
 * Channel maps are optional; most things do not need them, instead passing
1252
 * data in the [order that SDL expects](CategoryAudio#channel-layouts).
1253
 *
1254
 * Audio streams default to no remapping applied. This is represented by
1255
 * returning NULL, and does not signify an error.
1256
 *
1257
 * \param stream the SDL_AudioStream to query.
1258
 * \param count On output, set to number of channels in the map. Can be NULL.
1259
 * \returns an array of the current channel mapping, with as many elements as
1260
 *          the current output spec's channels, or NULL if default. This
1261
 *          should be freed with SDL_free() when it is no longer needed.
1262
 *
1263
 * \threadsafety It is safe to call this function from any thread, as it holds
1264
 *               a stream-specific mutex while running.
1265
 *
1266
 * \since This function is available since SDL 3.2.0.
1267
 *
1268
 * \sa SDL_SetAudioStreamInputChannelMap
1269
 */
1270
extern SDL_DECLSPEC int * SDLCALL SDL_GetAudioStreamOutputChannelMap(SDL_AudioStream *stream, int *count);
1271
1272
/**
1273
 * Set the current input channel map of an audio stream.
1274
 *
1275
 * Channel maps are optional; most things do not need them, instead passing
1276
 * data in the [order that SDL expects](CategoryAudio#channel-layouts).
1277
 *
1278
 * The input channel map reorders data that is added to a stream via
1279
 * SDL_PutAudioStreamData. Future calls to SDL_PutAudioStreamData must provide
1280
 * data in the new channel order.
1281
 *
1282
 * Each item in the array represents an input channel, and its value is the
1283
 * channel that it should be remapped to. To reverse a stereo signal's left
1284
 * and right values, you'd have an array of `{ 1, 0 }`. It is legal to remap
1285
 * multiple channels to the same thing, so `{ 1, 1 }` would duplicate the
1286
 * right channel to both channels of a stereo signal. An element in the
1287
 * channel map set to -1 instead of a valid channel will mute that channel,
1288
 * setting it to a silence value.
1289
 *
1290
 * You cannot change the number of channels through a channel map, just
1291
 * reorder/mute them.
1292
 *
1293
 * Data that was previously queued in the stream will still be operated on in
1294
 * the order that was current when it was added, which is to say you can put
1295
 * the end of a sound file in one order to a stream, change orders for the
1296
 * next sound file, and start putting that new data while the previous sound
1297
 * file is still queued, and everything will still play back correctly.
1298
 *
1299
 * Audio streams default to no remapping applied. Passing a NULL channel map
1300
 * is legal, and turns off remapping.
1301
 *
1302
 * SDL will copy the channel map; the caller does not have to save this array
1303
 * after this call.
1304
 *
1305
 * If `count` is not equal to the current number of channels in the audio
1306
 * stream's format, this will fail. This is a safety measure to make sure a
1307
 * race condition hasn't changed the format while this call is setting the
1308
 * channel map.
1309
 *
1310
 * Unlike attempting to change the stream's format, the input channel map on a
1311
 * stream bound to a recording device is permitted to change at any time; any
1312
 * data added to the stream from the device after this call will have the new
1313
 * mapping, but previously-added data will still have the prior mapping.
1314
 *
1315
 * \param stream the SDL_AudioStream to change.
1316
 * \param chmap the new channel map, NULL to reset to default.
1317
 * \param count The number of channels in the map.
1318
 * \returns true on success or false on failure; call SDL_GetError() for more
1319
 *          information.
1320
 *
1321
 * \threadsafety It is safe to call this function from any thread, as it holds
1322
 *               a stream-specific mutex while running. Don't change the
1323
 *               stream's format to have a different number of channels from a
1324
 *               a different thread at the same time, though!
1325
 *
1326
 * \since This function is available since SDL 3.2.0.
1327
 *
1328
 * \sa SDL_SetAudioStreamInputChannelMap
1329
 */
1330
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamInputChannelMap(SDL_AudioStream *stream, const int *chmap, int count);
1331
1332
/**
1333
 * Set the current output channel map of an audio stream.
1334
 *
1335
 * Channel maps are optional; most things do not need them, instead passing
1336
 * data in the [order that SDL expects](CategoryAudio#channel-layouts).
1337
 *
1338
 * The output channel map reorders data that leaving a stream via
1339
 * SDL_GetAudioStreamData.
1340
 *
1341
 * Each item in the array represents an input channel, and its value is the
1342
 * channel that it should be remapped to. To reverse a stereo signal's left
1343
 * and right values, you'd have an array of `{ 1, 0 }`. It is legal to remap
1344
 * multiple channels to the same thing, so `{ 1, 1 }` would duplicate the
1345
 * right channel to both channels of a stereo signal. An element in the
1346
 * channel map set to -1 instead of a valid channel will mute that channel,
1347
 * setting it to a silence value.
1348
 *
1349
 * You cannot change the number of channels through a channel map, just
1350
 * reorder/mute them.
1351
 *
1352
 * The output channel map can be changed at any time, as output remapping is
1353
 * applied during SDL_GetAudioStreamData.
1354
 *
1355
 * Audio streams default to no remapping applied. Passing a NULL channel map
1356
 * is legal, and turns off remapping.
1357
 *
1358
 * SDL will copy the channel map; the caller does not have to save this array
1359
 * after this call.
1360
 *
1361
 * If `count` is not equal to the current number of channels in the audio
1362
 * stream's format, this will fail. This is a safety measure to make sure a
1363
 * race condition hasn't changed the format while this call is setting the
1364
 * channel map.
1365
 *
1366
 * Unlike attempting to change the stream's format, the output channel map on
1367
 * a stream bound to a recording device is permitted to change at any time;
1368
 * any data added to the stream after this call will have the new mapping, but
1369
 * previously-added data will still have the prior mapping. When the channel
1370
 * map doesn't match the hardware's channel layout, SDL will convert the data
1371
 * before feeding it to the device for playback.
1372
 *
1373
 * \param stream the SDL_AudioStream to change.
1374
 * \param chmap the new channel map, NULL to reset to default.
1375
 * \param count The number of channels in the map.
1376
 * \returns true on success or false on failure; call SDL_GetError() for more
1377
 *          information.
1378
 *
1379
 * \threadsafety It is safe to call this function from any thread, as it holds
1380
 *               a stream-specific mutex while running. Don't change the
1381
 *               stream's format to have a different number of channels from a
1382
 *               a different thread at the same time, though!
1383
 *
1384
 * \since This function is available since SDL 3.2.0.
1385
 *
1386
 * \sa SDL_SetAudioStreamInputChannelMap
1387
 */
1388
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamOutputChannelMap(SDL_AudioStream *stream, const int *chmap, int count);
1389
1390
/**
1391
 * Add data to the stream.
1392
 *
1393
 * This data must match the format/channels/samplerate specified in the latest
1394
 * call to SDL_SetAudioStreamFormat, or the format specified when creating the
1395
 * stream if it hasn't been changed.
1396
 *
1397
 * Note that this call simply copies the unconverted data for later. This is
1398
 * different than SDL2, where data was converted during the Put call and the
1399
 * Get call would just dequeue the previously-converted data.
1400
 *
1401
 * \param stream the stream the audio data is being added to.
1402
 * \param buf a pointer to the audio data to add.
1403
 * \param len the number of bytes to write to the stream.
1404
 * \returns true on success or false on failure; call SDL_GetError() for more
1405
 *          information.
1406
 *
1407
 * \threadsafety It is safe to call this function from any thread, but if the
1408
 *               stream has a callback set, the caller might need to manage
1409
 *               extra locking.
1410
 *
1411
 * \since This function is available since SDL 3.2.0.
1412
 *
1413
 * \sa SDL_ClearAudioStream
1414
 * \sa SDL_FlushAudioStream
1415
 * \sa SDL_GetAudioStreamData
1416
 * \sa SDL_GetAudioStreamQueued
1417
 */
1418
extern SDL_DECLSPEC bool SDLCALL SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len);
1419
1420
/**
1421
 * Get converted/resampled data from the stream.
1422
 *
1423
 * The input/output data format/channels/samplerate is specified when creating
1424
 * the stream, and can be changed after creation by calling
1425
 * SDL_SetAudioStreamFormat.
1426
 *
1427
 * Note that any conversion and resampling necessary is done during this call,
1428
 * and SDL_PutAudioStreamData simply queues unconverted data for later. This
1429
 * is different than SDL2, where that work was done while inputting new data
1430
 * to the stream and requesting the output just copied the converted data.
1431
 *
1432
 * \param stream the stream the audio is being requested from.
1433
 * \param buf a buffer to fill with audio data.
1434
 * \param len the maximum number of bytes to fill.
1435
 * \returns the number of bytes read from the stream or -1 on failure; call
1436
 *          SDL_GetError() for more information.
1437
 *
1438
 * \threadsafety It is safe to call this function from any thread, but if the
1439
 *               stream has a callback set, the caller might need to manage
1440
 *               extra locking.
1441
 *
1442
 * \since This function is available since SDL 3.2.0.
1443
 *
1444
 * \sa SDL_ClearAudioStream
1445
 * \sa SDL_GetAudioStreamAvailable
1446
 * \sa SDL_PutAudioStreamData
1447
 */
1448
extern SDL_DECLSPEC int SDLCALL SDL_GetAudioStreamData(SDL_AudioStream *stream, void *buf, int len);
1449
1450
/**
1451
 * Get the number of converted/resampled bytes available.
1452
 *
1453
 * The stream may be buffering data behind the scenes until it has enough to
1454
 * resample correctly, so this number might be lower than what you expect, or
1455
 * even be zero. Add more data or flush the stream if you need the data now.
1456
 *
1457
 * If the stream has so much data that it would overflow an int, the return
1458
 * value is clamped to a maximum value, but no queued data is lost; if there
1459
 * are gigabytes of data queued, the app might need to read some of it with
1460
 * SDL_GetAudioStreamData before this function's return value is no longer
1461
 * clamped.
1462
 *
1463
 * \param stream the audio stream to query.
1464
 * \returns the number of converted/resampled bytes available or -1 on
1465
 *          failure; call SDL_GetError() for more information.
1466
 *
1467
 * \threadsafety It is safe to call this function from any thread.
1468
 *
1469
 * \since This function is available since SDL 3.2.0.
1470
 *
1471
 * \sa SDL_GetAudioStreamData
1472
 * \sa SDL_PutAudioStreamData
1473
 */
1474
extern SDL_DECLSPEC int SDLCALL SDL_GetAudioStreamAvailable(SDL_AudioStream *stream);
1475
1476
1477
/**
1478
 * Get the number of bytes currently queued.
1479
 *
1480
 * This is the number of bytes put into a stream as input, not the number that
1481
 * can be retrieved as output. Because of several details, it's not possible
1482
 * to calculate one number directly from the other. If you need to know how
1483
 * much usable data can be retrieved right now, you should use
1484
 * SDL_GetAudioStreamAvailable() and not this function.
1485
 *
1486
 * Note that audio streams can change their input format at any time, even if
1487
 * there is still data queued in a different format, so the returned byte
1488
 * count will not necessarily match the number of _sample frames_ available.
1489
 * Users of this API should be aware of format changes they make when feeding
1490
 * a stream and plan accordingly.
1491
 *
1492
 * Queued data is not converted until it is consumed by
1493
 * SDL_GetAudioStreamData, so this value should be representative of the exact
1494
 * data that was put into the stream.
1495
 *
1496
 * If the stream has so much data that it would overflow an int, the return
1497
 * value is clamped to a maximum value, but no queued data is lost; if there
1498
 * are gigabytes of data queued, the app might need to read some of it with
1499
 * SDL_GetAudioStreamData before this function's return value is no longer
1500
 * clamped.
1501
 *
1502
 * \param stream the audio stream to query.
1503
 * \returns the number of bytes queued or -1 on failure; call SDL_GetError()
1504
 *          for more information.
1505
 *
1506
 * \threadsafety It is safe to call this function from any thread.
1507
 *
1508
 * \since This function is available since SDL 3.2.0.
1509
 *
1510
 * \sa SDL_PutAudioStreamData
1511
 * \sa SDL_ClearAudioStream
1512
 */
1513
extern SDL_DECLSPEC int SDLCALL SDL_GetAudioStreamQueued(SDL_AudioStream *stream);
1514
1515
1516
/**
1517
 * Tell the stream that you're done sending data, and anything being buffered
1518
 * should be converted/resampled and made available immediately.
1519
 *
1520
 * It is legal to add more data to a stream after flushing, but there may be
1521
 * audio gaps in the output. Generally this is intended to signal the end of
1522
 * input, so the complete output becomes available.
1523
 *
1524
 * \param stream the audio stream to flush.
1525
 * \returns true on success or false on failure; call SDL_GetError() for more
1526
 *          information.
1527
 *
1528
 * \threadsafety It is safe to call this function from any thread.
1529
 *
1530
 * \since This function is available since SDL 3.2.0.
1531
 *
1532
 * \sa SDL_PutAudioStreamData
1533
 */
1534
extern SDL_DECLSPEC bool SDLCALL SDL_FlushAudioStream(SDL_AudioStream *stream);
1535
1536
/**
1537
 * Clear any pending data in the stream.
1538
 *
1539
 * This drops any queued data, so there will be nothing to read from the
1540
 * stream until more is added.
1541
 *
1542
 * \param stream the audio stream to clear.
1543
 * \returns true on success or false on failure; call SDL_GetError() for more
1544
 *          information.
1545
 *
1546
 * \threadsafety It is safe to call this function from any thread.
1547
 *
1548
 * \since This function is available since SDL 3.2.0.
1549
 *
1550
 * \sa SDL_GetAudioStreamAvailable
1551
 * \sa SDL_GetAudioStreamData
1552
 * \sa SDL_GetAudioStreamQueued
1553
 * \sa SDL_PutAudioStreamData
1554
 */
1555
extern SDL_DECLSPEC bool SDLCALL SDL_ClearAudioStream(SDL_AudioStream *stream);
1556
1557
/**
1558
 * Use this function to pause audio playback on the audio device associated
1559
 * with an audio stream.
1560
 *
1561
 * This function pauses audio processing for a given device. Any bound audio
1562
 * streams will not progress, and no audio will be generated. Pausing one
1563
 * device does not prevent other unpaused devices from running.
1564
 *
1565
 * Pausing a device can be useful to halt all audio without unbinding all the
1566
 * audio streams. This might be useful while a game is paused, or a level is
1567
 * loading, etc.
1568
 *
1569
 * \param stream the audio stream associated with the audio device to pause.
1570
 * \returns true on success or false on failure; call SDL_GetError() for more
1571
 *          information.
1572
 *
1573
 * \threadsafety It is safe to call this function from any thread.
1574
 *
1575
 * \since This function is available since SDL 3.2.0.
1576
 *
1577
 * \sa SDL_ResumeAudioStreamDevice
1578
 */
1579
extern SDL_DECLSPEC bool SDLCALL SDL_PauseAudioStreamDevice(SDL_AudioStream *stream);
1580
1581
/**
1582
 * Use this function to unpause audio playback on the audio device associated
1583
 * with an audio stream.
1584
 *
1585
 * This function unpauses audio processing for a given device that has
1586
 * previously been paused. Once unpaused, any bound audio streams will begin
1587
 * to progress again, and audio can be generated.
1588
 *
1589
 * Remember, SDL_OpenAudioDeviceStream opens device in a paused state, so this
1590
 * function call is required for audio playback to begin on such device.
1591
 *
1592
 * \param stream the audio stream associated with the audio device to resume.
1593
 * \returns true on success or false on failure; call SDL_GetError() for more
1594
 *          information.
1595
 *
1596
 * \threadsafety It is safe to call this function from any thread.
1597
 *
1598
 * \since This function is available since SDL 3.2.0.
1599
 *
1600
 * \sa SDL_PauseAudioStreamDevice
1601
 */
1602
extern SDL_DECLSPEC bool SDLCALL SDL_ResumeAudioStreamDevice(SDL_AudioStream *stream);
1603
1604
/**
1605
 * Use this function to query if an audio device associated with a stream is
1606
 * paused.
1607
 *
1608
 * Unlike in SDL2, audio devices start in an _unpaused_ state, since an app
1609
 * has to bind a stream before any audio will flow.
1610
 *
1611
 * \param stream the audio stream associated with the audio device to query.
1612
 * \returns true if device is valid and paused, false otherwise.
1613
 *
1614
 * \threadsafety It is safe to call this function from any thread.
1615
 *
1616
 * \since This function is available since SDL 3.2.0.
1617
 *
1618
 * \sa SDL_PauseAudioStreamDevice
1619
 * \sa SDL_ResumeAudioStreamDevice
1620
 */
1621
extern SDL_DECLSPEC bool SDLCALL SDL_AudioStreamDevicePaused(SDL_AudioStream *stream);
1622
1623
1624
/**
1625
 * Lock an audio stream for serialized access.
1626
 *
1627
 * Each SDL_AudioStream has an internal mutex it uses to protect its data
1628
 * structures from threading conflicts. This function allows an app to lock
1629
 * that mutex, which could be useful if registering callbacks on this stream.
1630
 *
1631
 * One does not need to lock a stream to use in it most cases, as the stream
1632
 * manages this lock internally. However, this lock is held during callbacks,
1633
 * which may run from arbitrary threads at any time, so if an app needs to
1634
 * protect shared data during those callbacks, locking the stream guarantees
1635
 * that the callback is not running while the lock is held.
1636
 *
1637
 * As this is just a wrapper over SDL_LockMutex for an internal lock; it has
1638
 * all the same attributes (recursive locks are allowed, etc).
1639
 *
1640
 * \param stream the audio stream to lock.
1641
 * \returns true on success or false on failure; call SDL_GetError() for more
1642
 *          information.
1643
 *
1644
 * \threadsafety It is safe to call this function from any thread.
1645
 *
1646
 * \since This function is available since SDL 3.2.0.
1647
 *
1648
 * \sa SDL_UnlockAudioStream
1649
 */
1650
extern SDL_DECLSPEC bool SDLCALL SDL_LockAudioStream(SDL_AudioStream *stream);
1651
1652
1653
/**
1654
 * Unlock an audio stream for serialized access.
1655
 *
1656
 * This unlocks an audio stream after a call to SDL_LockAudioStream.
1657
 *
1658
 * \param stream the audio stream to unlock.
1659
 * \returns true on success or false on failure; call SDL_GetError() for more
1660
 *          information.
1661
 *
1662
 * \threadsafety You should only call this from the same thread that
1663
 *               previously called SDL_LockAudioStream.
1664
 *
1665
 * \since This function is available since SDL 3.2.0.
1666
 *
1667
 * \sa SDL_LockAudioStream
1668
 */
1669
extern SDL_DECLSPEC bool SDLCALL SDL_UnlockAudioStream(SDL_AudioStream *stream);
1670
1671
/**
1672
 * A callback that fires when data passes through an SDL_AudioStream.
1673
 *
1674
 * Apps can (optionally) register a callback with an audio stream that is
1675
 * called when data is added with SDL_PutAudioStreamData, or requested with
1676
 * SDL_GetAudioStreamData.
1677
 *
1678
 * Two values are offered here: one is the amount of additional data needed to
1679
 * satisfy the immediate request (which might be zero if the stream already
1680
 * has enough data queued) and the other is the total amount being requested.
1681
 * In a Get call triggering a Put callback, these values can be different. In
1682
 * a Put call triggering a Get callback, these values are always the same.
1683
 *
1684
 * Byte counts might be slightly overestimated due to buffering or resampling,
1685
 * and may change from call to call.
1686
 *
1687
 * This callback is not required to do anything. Generally this is useful for
1688
 * adding/reading data on demand, and the app will often put/get data as
1689
 * appropriate, but the system goes on with the data currently available to it
1690
 * if this callback does nothing.
1691
 *
1692
 * \param stream the SDL audio stream associated with this callback.
1693
 * \param additional_amount the amount of data, in bytes, that is needed right
1694
 *                          now.
1695
 * \param total_amount the total amount of data requested, in bytes, that is
1696
 *                     requested or available.
1697
 * \param userdata an opaque pointer provided by the app for their personal
1698
 *                 use.
1699
 *
1700
 * \threadsafety This callbacks may run from any thread, so if you need to
1701
 *               protect shared data, you should use SDL_LockAudioStream to
1702
 *               serialize access; this lock will be held before your callback
1703
 *               is called, so your callback does not need to manage the lock
1704
 *               explicitly.
1705
 *
1706
 * \since This datatype is available since SDL 3.2.0.
1707
 *
1708
 * \sa SDL_SetAudioStreamGetCallback
1709
 * \sa SDL_SetAudioStreamPutCallback
1710
 */
1711
typedef void (SDLCALL *SDL_AudioStreamCallback)(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount);
1712
1713
/**
1714
 * Set a callback that runs when data is requested from an audio stream.
1715
 *
1716
 * This callback is called _before_ data is obtained from the stream, giving
1717
 * the callback the chance to add more on-demand.
1718
 *
1719
 * The callback can (optionally) call SDL_PutAudioStreamData() to add more
1720
 * audio to the stream during this call; if needed, the request that triggered
1721
 * this callback will obtain the new data immediately.
1722
 *
1723
 * The callback's `additional_amount` argument is roughly how many bytes of
1724
 * _unconverted_ data (in the stream's input format) is needed by the caller,
1725
 * although this may overestimate a little for safety. This takes into account
1726
 * how much is already in the stream and only asks for any extra necessary to
1727
 * resolve the request, which means the callback may be asked for zero bytes,
1728
 * and a different amount on each call.
1729
 *
1730
 * The callback is not required to supply exact amounts; it is allowed to
1731
 * supply too much or too little or none at all. The caller will get what's
1732
 * available, up to the amount they requested, regardless of this callback's
1733
 * outcome.
1734
 *
1735
 * Clearing or flushing an audio stream does not call this callback.
1736
 *
1737
 * This function obtains the stream's lock, which means any existing callback
1738
 * (get or put) in progress will finish running before setting the new
1739
 * callback.
1740
 *
1741
 * Setting a NULL function turns off the callback.
1742
 *
1743
 * \param stream the audio stream to set the new callback on.
1744
 * \param callback the new callback function to call when data is requested
1745
 *                 from the stream.
1746
 * \param userdata an opaque pointer provided to the callback for its own
1747
 *                 personal use.
1748
 * \returns true on success or false on failure; call SDL_GetError() for more
1749
 *          information. This only fails if `stream` is NULL.
1750
 *
1751
 * \threadsafety It is safe to call this function from any thread.
1752
 *
1753
 * \since This function is available since SDL 3.2.0.
1754
 *
1755
 * \sa SDL_SetAudioStreamPutCallback
1756
 */
1757
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata);
1758
1759
/**
1760
 * Set a callback that runs when data is added to an audio stream.
1761
 *
1762
 * This callback is called _after_ the data is added to the stream, giving the
1763
 * callback the chance to obtain it immediately.
1764
 *
1765
 * The callback can (optionally) call SDL_GetAudioStreamData() to obtain audio
1766
 * from the stream during this call.
1767
 *
1768
 * The callback's `additional_amount` argument is how many bytes of
1769
 * _converted_ data (in the stream's output format) was provided by the
1770
 * caller, although this may underestimate a little for safety. This value
1771
 * might be less than what is currently available in the stream, if data was
1772
 * already there, and might be less than the caller provided if the stream
1773
 * needs to keep a buffer to aid in resampling. Which means the callback may
1774
 * be provided with zero bytes, and a different amount on each call.
1775
 *
1776
 * The callback may call SDL_GetAudioStreamAvailable to see the total amount
1777
 * currently available to read from the stream, instead of the total provided
1778
 * by the current call.
1779
 *
1780
 * The callback is not required to obtain all data. It is allowed to read less
1781
 * or none at all. Anything not read now simply remains in the stream for
1782
 * later access.
1783
 *
1784
 * Clearing or flushing an audio stream does not call this callback.
1785
 *
1786
 * This function obtains the stream's lock, which means any existing callback
1787
 * (get or put) in progress will finish running before setting the new
1788
 * callback.
1789
 *
1790
 * Setting a NULL function turns off the callback.
1791
 *
1792
 * \param stream the audio stream to set the new callback on.
1793
 * \param callback the new callback function to call when data is added to the
1794
 *                 stream.
1795
 * \param userdata an opaque pointer provided to the callback for its own
1796
 *                 personal use.
1797
 * \returns true on success or false on failure; call SDL_GetError() for more
1798
 *          information. This only fails if `stream` is NULL.
1799
 *
1800
 * \threadsafety It is safe to call this function from any thread.
1801
 *
1802
 * \since This function is available since SDL 3.2.0.
1803
 *
1804
 * \sa SDL_SetAudioStreamGetCallback
1805
 */
1806
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata);
1807
1808
1809
/**
1810
 * Free an audio stream.
1811
 *
1812
 * This will release all allocated data, including any audio that is still
1813
 * queued. You do not need to manually clear the stream first.
1814
 *
1815
 * If this stream was bound to an audio device, it is unbound during this
1816
 * call. If this stream was created with SDL_OpenAudioDeviceStream, the audio
1817
 * device that was opened alongside this stream's creation will be closed,
1818
 * too.
1819
 *
1820
 * \param stream the audio stream to destroy.
1821
 *
1822
 * \threadsafety It is safe to call this function from any thread.
1823
 *
1824
 * \since This function is available since SDL 3.2.0.
1825
 *
1826
 * \sa SDL_CreateAudioStream
1827
 */
1828
extern SDL_DECLSPEC void SDLCALL SDL_DestroyAudioStream(SDL_AudioStream *stream);
1829
1830
1831
/**
1832
 * Convenience function for straightforward audio init for the common case.
1833
 *
1834
 * If all your app intends to do is provide a single source of PCM audio, this
1835
 * function allows you to do all your audio setup in a single call.
1836
 *
1837
 * This is also intended to be a clean means to migrate apps from SDL2.
1838
 *
1839
 * This function will open an audio device, create a stream and bind it.
1840
 * Unlike other methods of setup, the audio device will be closed when this
1841
 * stream is destroyed, so the app can treat the returned SDL_AudioStream as
1842
 * the only object needed to manage audio playback.
1843
 *
1844
 * Also unlike other functions, the audio device begins paused. This is to map
1845
 * more closely to SDL2-style behavior, since there is no extra step here to
1846
 * bind a stream to begin audio flowing. The audio device should be resumed
1847
 * with `SDL_ResumeAudioStreamDevice(stream);`
1848
 *
1849
 * This function works with both playback and recording devices.
1850
 *
1851
 * The `spec` parameter represents the app's side of the audio stream. That
1852
 * is, for recording audio, this will be the output format, and for playing
1853
 * audio, this will be the input format. If spec is NULL, the system will
1854
 * choose the format, and the app can use SDL_GetAudioStreamFormat() to obtain
1855
 * this information later.
1856
 *
1857
 * If you don't care about opening a specific audio device, you can (and
1858
 * probably _should_), use SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK for playback and
1859
 * SDL_AUDIO_DEVICE_DEFAULT_RECORDING for recording.
1860
 *
1861
 * One can optionally provide a callback function; if NULL, the app is
1862
 * expected to queue audio data for playback (or unqueue audio data if
1863
 * capturing). Otherwise, the callback will begin to fire once the device is
1864
 * unpaused.
1865
 *
1866
 * Destroying the returned stream with SDL_DestroyAudioStream will also close
1867
 * the audio device associated with this stream.
1868
 *
1869
 * \param devid an audio device to open, or SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK
1870
 *              or SDL_AUDIO_DEVICE_DEFAULT_RECORDING.
1871
 * \param spec the audio stream's data format. Can be NULL.
1872
 * \param callback a callback where the app will provide new data for
1873
 *                 playback, or receive new data for recording. Can be NULL,
1874
 *                 in which case the app will need to call
1875
 *                 SDL_PutAudioStreamData or SDL_GetAudioStreamData as
1876
 *                 necessary.
1877
 * \param userdata app-controlled pointer passed to callback. Can be NULL.
1878
 *                 Ignored if callback is NULL.
1879
 * \returns an audio stream on success, ready to use, or NULL on failure; call
1880
 *          SDL_GetError() for more information. When done with this stream,
1881
 *          call SDL_DestroyAudioStream to free resources and close the
1882
 *          device.
1883
 *
1884
 * \threadsafety It is safe to call this function from any thread.
1885
 *
1886
 * \since This function is available since SDL 3.2.0.
1887
 *
1888
 * \sa SDL_GetAudioStreamDevice
1889
 * \sa SDL_ResumeAudioStreamDevice
1890
 */
1891
extern SDL_DECLSPEC SDL_AudioStream * SDLCALL SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata);
1892
1893
/**
1894
 * A callback that fires when data is about to be fed to an audio device.
1895
 *
1896
 * This is useful for accessing the final mix, perhaps for writing a
1897
 * visualizer or applying a final effect to the audio data before playback.
1898
 *
1899
 * This callback should run as quickly as possible and not block for any
1900
 * significant time, as this callback delays submission of data to the audio
1901
 * device, which can cause audio playback problems.
1902
 *
1903
 * The postmix callback _must_ be able to handle any audio data format
1904
 * specified in `spec`, which can change between callbacks if the audio device
1905
 * changed. However, this only covers frequency and channel count; data is
1906
 * always provided here in SDL_AUDIO_F32 format.
1907
 *
1908
 * The postmix callback runs _after_ logical device gain and audiostream gain
1909
 * have been applied, which is to say you can make the output data louder at
1910
 * this point than the gain settings would suggest.
1911
 *
1912
 * \param userdata a pointer provided by the app through
1913
 *                 SDL_SetAudioPostmixCallback, for its own use.
1914
 * \param spec the current format of audio that is to be submitted to the
1915
 *             audio device.
1916
 * \param buffer the buffer of audio samples to be submitted. The callback can
1917
 *               inspect and/or modify this data.
1918
 * \param buflen the size of `buffer` in bytes.
1919
 *
1920
 * \threadsafety This will run from a background thread owned by SDL. The
1921
 *               application is responsible for locking resources the callback
1922
 *               touches that need to be protected.
1923
 *
1924
 * \since This datatype is available since SDL 3.2.0.
1925
 *
1926
 * \sa SDL_SetAudioPostmixCallback
1927
 */
1928
typedef void (SDLCALL *SDL_AudioPostmixCallback)(void *userdata, const SDL_AudioSpec *spec, float *buffer, int buflen);
1929
1930
/**
1931
 * Set a callback that fires when data is about to be fed to an audio device.
1932
 *
1933
 * This is useful for accessing the final mix, perhaps for writing a
1934
 * visualizer or applying a final effect to the audio data before playback.
1935
 *
1936
 * The buffer is the final mix of all bound audio streams on an opened device;
1937
 * this callback will fire regularly for any device that is both opened and
1938
 * unpaused. If there is no new data to mix, either because no streams are
1939
 * bound to the device or all the streams are empty, this callback will still
1940
 * fire with the entire buffer set to silence.
1941
 *
1942
 * This callback is allowed to make changes to the data; the contents of the
1943
 * buffer after this call is what is ultimately passed along to the hardware.
1944
 *
1945
 * The callback is always provided the data in float format (values from -1.0f
1946
 * to 1.0f), but the number of channels or sample rate may be different than
1947
 * the format the app requested when opening the device; SDL might have had to
1948
 * manage a conversion behind the scenes, or the playback might have jumped to
1949
 * new physical hardware when a system default changed, etc. These details may
1950
 * change between calls. Accordingly, the size of the buffer might change
1951
 * between calls as well.
1952
 *
1953
 * This callback can run at any time, and from any thread; if you need to
1954
 * serialize access to your app's data, you should provide and use a mutex or
1955
 * other synchronization device.
1956
 *
1957
 * All of this to say: there are specific needs this callback can fulfill, but
1958
 * it is not the simplest interface. Apps should generally provide audio in
1959
 * their preferred format through an SDL_AudioStream and let SDL handle the
1960
 * difference.
1961
 *
1962
 * This function is extremely time-sensitive; the callback should do the least
1963
 * amount of work possible and return as quickly as it can. The longer the
1964
 * callback runs, the higher the risk of audio dropouts or other problems.
1965
 *
1966
 * This function will block until the audio device is in between iterations,
1967
 * so any existing callback that might be running will finish before this
1968
 * function sets the new callback and returns.
1969
 *
1970
 * Setting a NULL callback function disables any previously-set callback.
1971
 *
1972
 * \param devid the ID of an opened audio device.
1973
 * \param callback a callback function to be called. Can be NULL.
1974
 * \param userdata app-controlled pointer passed to callback. Can be NULL.
1975
 * \returns true on success or false on failure; call SDL_GetError() for more
1976
 *          information.
1977
 *
1978
 * \threadsafety It is safe to call this function from any thread.
1979
 *
1980
 * \since This function is available since SDL 3.2.0.
1981
 */
1982
extern SDL_DECLSPEC bool SDLCALL SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata);
1983
1984
1985
/**
1986
 * Load the audio data of a WAVE file into memory.
1987
 *
1988
 * Loading a WAVE file requires `src`, `spec`, `audio_buf` and `audio_len` to
1989
 * be valid pointers. The entire data portion of the file is then loaded into
1990
 * memory and decoded if necessary.
1991
 *
1992
 * Supported formats are RIFF WAVE files with the formats PCM (8, 16, 24, and
1993
 * 32 bits), IEEE Float (32 bits), Microsoft ADPCM and IMA ADPCM (4 bits), and
1994
 * A-law and mu-law (8 bits). Other formats are currently unsupported and
1995
 * cause an error.
1996
 *
1997
 * If this function succeeds, the return value is zero and the pointer to the
1998
 * audio data allocated by the function is written to `audio_buf` and its
1999
 * length in bytes to `audio_len`. The SDL_AudioSpec members `freq`,
2000
 * `channels`, and `format` are set to the values of the audio data in the
2001
 * buffer.
2002
 *
2003
 * It's necessary to use SDL_free() to free the audio data returned in
2004
 * `audio_buf` when it is no longer used.
2005
 *
2006
 * Because of the underspecification of the .WAV format, there are many
2007
 * problematic files in the wild that cause issues with strict decoders. To
2008
 * provide compatibility with these files, this decoder is lenient in regards
2009
 * to the truncation of the file, the fact chunk, and the size of the RIFF
2010
 * chunk. The hints `SDL_HINT_WAVE_RIFF_CHUNK_SIZE`,
2011
 * `SDL_HINT_WAVE_TRUNCATION`, and `SDL_HINT_WAVE_FACT_CHUNK` can be used to
2012
 * tune the behavior of the loading process.
2013
 *
2014
 * Any file that is invalid (due to truncation, corruption, or wrong values in
2015
 * the headers), too big, or unsupported causes an error. Additionally, any
2016
 * critical I/O error from the data source will terminate the loading process
2017
 * with an error. The function returns NULL on error and in all cases (with
2018
 * the exception of `src` being NULL), an appropriate error message will be
2019
 * set.
2020
 *
2021
 * It is required that the data source supports seeking.
2022
 *
2023
 * Example:
2024
 *
2025
 * ```c
2026
 * SDL_LoadWAV_IO(SDL_IOFromFile("sample.wav", "rb"), true, &spec, &buf, &len);
2027
 * ```
2028
 *
2029
 * Note that the SDL_LoadWAV function does this same thing for you, but in a
2030
 * less messy way:
2031
 *
2032
 * ```c
2033
 * SDL_LoadWAV("sample.wav", &spec, &buf, &len);
2034
 * ```
2035
 *
2036
 * \param src the data source for the WAVE data.
2037
 * \param closeio if true, calls SDL_CloseIO() on `src` before returning, even
2038
 *                in the case of an error.
2039
 * \param spec a pointer to an SDL_AudioSpec that will be set to the WAVE
2040
 *             data's format details on successful return.
2041
 * \param audio_buf a pointer filled with the audio data, allocated by the
2042
 *                  function.
2043
 * \param audio_len a pointer filled with the length of the audio data buffer
2044
 *                  in bytes.
2045
 * \returns true on success. `audio_buf` will be filled with a pointer to an
2046
 *          allocated buffer containing the audio data, and `audio_len` is
2047
 *          filled with the length of that audio buffer in bytes.
2048
 *
2049
 *          This function returns false if the .WAV file cannot be opened,
2050
 *          uses an unknown data format, or is corrupt; call SDL_GetError()
2051
 *          for more information.
2052
 *
2053
 *          When the application is done with the data returned in
2054
 *          `audio_buf`, it should call SDL_free() to dispose of it.
2055
 *
2056
 * \threadsafety It is safe to call this function from any thread.
2057
 *
2058
 * \since This function is available since SDL 3.2.0.
2059
 *
2060
 * \sa SDL_free
2061
 * \sa SDL_LoadWAV
2062
 */
2063
extern SDL_DECLSPEC bool SDLCALL SDL_LoadWAV_IO(SDL_IOStream *src, bool closeio, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len);
2064
2065
/**
2066
 * Loads a WAV from a file path.
2067
 *
2068
 * This is a convenience function that is effectively the same as:
2069
 *
2070
 * ```c
2071
 * SDL_LoadWAV_IO(SDL_IOFromFile(path, "rb"), true, spec, audio_buf, audio_len);
2072
 * ```
2073
 *
2074
 * \param path the file path of the WAV file to open.
2075
 * \param spec a pointer to an SDL_AudioSpec that will be set to the WAVE
2076
 *             data's format details on successful return.
2077
 * \param audio_buf a pointer filled with the audio data, allocated by the
2078
 *                  function.
2079
 * \param audio_len a pointer filled with the length of the audio data buffer
2080
 *                  in bytes.
2081
 * \returns true on success. `audio_buf` will be filled with a pointer to an
2082
 *          allocated buffer containing the audio data, and `audio_len` is
2083
 *          filled with the length of that audio buffer in bytes.
2084
 *
2085
 *          This function returns false if the .WAV file cannot be opened,
2086
 *          uses an unknown data format, or is corrupt; call SDL_GetError()
2087
 *          for more information.
2088
 *
2089
 *          When the application is done with the data returned in
2090
 *          `audio_buf`, it should call SDL_free() to dispose of it.
2091
 *
2092
 * \threadsafety It is safe to call this function from any thread.
2093
 *
2094
 * \since This function is available since SDL 3.2.0.
2095
 *
2096
 * \sa SDL_free
2097
 * \sa SDL_LoadWAV_IO
2098
 */
2099
extern SDL_DECLSPEC bool SDLCALL SDL_LoadWAV(const char *path, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len);
2100
2101
/**
2102
 * Mix audio data in a specified format.
2103
 *
2104
 * This takes an audio buffer `src` of `len` bytes of `format` data and mixes
2105
 * it into `dst`, performing addition, volume adjustment, and overflow
2106
 * clipping. The buffer pointed to by `dst` must also be `len` bytes of
2107
 * `format` data.
2108
 *
2109
 * This is provided for convenience -- you can mix your own audio data.
2110
 *
2111
 * Do not use this function for mixing together more than two streams of
2112
 * sample data. The output from repeated application of this function may be
2113
 * distorted by clipping, because there is no accumulator with greater range
2114
 * than the input (not to mention this being an inefficient way of doing it).
2115
 *
2116
 * It is a common misconception that this function is required to write audio
2117
 * data to an output stream in an audio callback. While you can do that,
2118
 * SDL_MixAudio() is really only needed when you're mixing a single audio
2119
 * stream with a volume adjustment.
2120
 *
2121
 * \param dst the destination for the mixed audio.
2122
 * \param src the source audio buffer to be mixed.
2123
 * \param format the SDL_AudioFormat structure representing the desired audio
2124
 *               format.
2125
 * \param len the length of the audio buffer in bytes.
2126
 * \param volume ranges from 0.0 - 1.0, and should be set to 1.0 for full
2127
 *               audio volume.
2128
 * \returns true on success or false on failure; call SDL_GetError() for more
2129
 *          information.
2130
 *
2131
 * \threadsafety It is safe to call this function from any thread.
2132
 *
2133
 * \since This function is available since SDL 3.2.0.
2134
 */
2135
extern SDL_DECLSPEC bool SDLCALL SDL_MixAudio(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, Uint32 len, float volume);
2136
2137
/**
2138
 * Convert some audio data of one format to another format.
2139
 *
2140
 * Please note that this function is for convenience, but should not be used
2141
 * to resample audio in blocks, as it will introduce audio artifacts on the
2142
 * boundaries. You should only use this function if you are converting audio
2143
 * data in its entirety in one call. If you want to convert audio in smaller
2144
 * chunks, use an SDL_AudioStream, which is designed for this situation.
2145
 *
2146
 * Internally, this function creates and destroys an SDL_AudioStream on each
2147
 * use, so it's also less efficient than using one directly, if you need to
2148
 * convert multiple times.
2149
 *
2150
 * \param src_spec the format details of the input audio.
2151
 * \param src_data the audio data to be converted.
2152
 * \param src_len the len of src_data.
2153
 * \param dst_spec the format details of the output audio.
2154
 * \param dst_data will be filled with a pointer to converted audio data,
2155
 *                 which should be freed with SDL_free(). On error, it will be
2156
 *                 NULL.
2157
 * \param dst_len will be filled with the len of dst_data.
2158
 * \returns true on success or false on failure; call SDL_GetError() for more
2159
 *          information.
2160
 *
2161
 * \threadsafety It is safe to call this function from any thread.
2162
 *
2163
 * \since This function is available since SDL 3.2.0.
2164
 */
2165
extern SDL_DECLSPEC bool SDLCALL SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len);
2166
2167
/**
2168
 * Get the human readable name of an audio format.
2169
 *
2170
 * \param format the audio format to query.
2171
 * \returns the human readable name of the specified audio format or
2172
 *          "SDL_AUDIO_UNKNOWN" if the format isn't recognized.
2173
 *
2174
 * \threadsafety It is safe to call this function from any thread.
2175
 *
2176
 * \since This function is available since SDL 3.2.0.
2177
 */
2178
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAudioFormatName(SDL_AudioFormat format);
2179
2180
/**
2181
 * Get the appropriate memset value for silencing an audio format.
2182
 *
2183
 * The value returned by this function can be used as the second argument to
2184
 * memset (or SDL_memset) to set an audio buffer in a specific format to
2185
 * silence.
2186
 *
2187
 * \param format the audio data format to query.
2188
 * \returns a byte value that can be passed to memset.
2189
 *
2190
 * \threadsafety It is safe to call this function from any thread.
2191
 *
2192
 * \since This function is available since SDL 3.2.0.
2193
 */
2194
extern SDL_DECLSPEC int SDLCALL SDL_GetSilenceValueForFormat(SDL_AudioFormat format);
2195
2196
2197
/* Ends C function definitions when using C++ */
2198
#ifdef __cplusplus
2199
}
2200
#endif
2201
#include <SDL3/SDL_close_code.h>
2202
2203
#endif /* SDL_audio_h_ */
/opt/homebrew/include/SDL3/SDL_bits.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryBits
24
 *
25
 * Functions for fiddling with bits and bitmasks.
26
 */
27
28
#ifndef SDL_bits_h_
29
#define SDL_bits_h_
30
31
#include <SDL3/SDL_stdinc.h>
32
33
#include <SDL3/SDL_begin_code.h>
34
/* Set up for C function definitions, even when using C++ */
35
#ifdef __cplusplus
36
extern "C" {
37
#endif
38
39
#if defined(__WATCOMC__) && defined(__386__)
40
extern __inline int _SDL_bsr_watcom(Uint32);
41
#pragma aux _SDL_bsr_watcom = \
42
    "bsr eax, eax" \
43
    parm [eax] nomemory \
44
    value [eax] \
45
    modify exact [eax] nomemory;
46
#endif
47
48
/**
49
 * Get the index of the most significant (set) bit in a 32-bit number.
50
 *
51
 * Result is undefined when called with 0. This operation can also be stated
52
 * as "count leading zeroes" and "log base 2".
53
 *
54
 * Note that this is a forced-inline function in a header, and not a public
55
 * API function available in the SDL library (which is to say, the code is
56
 * embedded in the calling program and the linker and dynamic loader will not
57
 * be able to find this function inside SDL itself).
58
 *
59
 * \param x the 32-bit value to examine.
60
 * \returns the index of the most significant bit, or -1 if the value is 0.
61
 *
62
 * \threadsafety It is safe to call this function from any thread.
63
 *
64
 * \since This function is available since SDL 3.2.0.
65
 */
66
SDL_FORCE_INLINE int SDL_MostSignificantBitIndex32(Uint32 x)
67
0
{
68
0
#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
69
0
    /* Count Leading Zeroes builtin in GCC.
70
0
     * http://gcc.gnu.org/onlinedocs/gcc-4.3.4/gcc/Other-Builtins.html
71
0
     */
72
0
    if (x == 0) {
73
0
        return -1;
74
0
    }
75
0
    return 31 - __builtin_clz(x);
76
0
#elif defined(__WATCOMC__) && defined(__386__)
77
0
    if (x == 0) {
78
0
        return -1;
79
0
    }
80
0
    return _SDL_bsr_watcom(x);
81
0
#elif defined(_MSC_VER) && _MSC_VER >= 1400
82
0
    unsigned long index;
83
0
    if (_BitScanReverse(&index, x)) {
84
0
        return (int)index;
85
0
    }
86
0
    return -1;
87
0
#else
88
0
    /* Based off of Bit Twiddling Hacks by Sean Eron Anderson
89
0
     * <seander@cs.stanford.edu>, released in the public domain.
90
0
     * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
91
0
     */
92
0
    const Uint32 b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
93
0
    const int    S[] = {1, 2, 4, 8, 16};
94
0
95
0
    int msbIndex = 0;
96
0
    int i;
97
0
98
0
    if (x == 0) {
99
0
        return -1;
100
0
    }
101
0
102
0
    for (i = 4; i >= 0; i--)
103
0
    {
104
0
        if (x & b[i])
105
0
        {
106
0
            x >>= S[i];
107
0
            msbIndex |= S[i];
108
0
        }
109
0
    }
110
0
111
0
    return msbIndex;
112
0
#endif
113
0
}
Unexecuted instantiation: emulator.cpp:_ZL29SDL_MostSignificantBitIndex32j
Unexecuted instantiation: sound.cpp:_ZL29SDL_MostSignificantBitIndex32j
Unexecuted instantiation: ay8912.cpp:_ZL29SDL_MostSignificantBitIndex32j
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL29SDL_MostSignificantBitIndex32j
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL29SDL_MostSignificantBitIndex32j
114
115
/**
116
 * Determine if a unsigned 32-bit value has exactly one bit set.
117
 *
118
 * If there are no bits set (`x` is zero), or more than one bit set, this
119
 * returns false. If any one bit is exclusively set, this returns true.
120
 *
121
 * Note that this is a forced-inline function in a header, and not a public
122
 * API function available in the SDL library (which is to say, the code is
123
 * embedded in the calling program and the linker and dynamic loader will not
124
 * be able to find this function inside SDL itself).
125
 *
126
 * \param x the 32-bit value to examine.
127
 * \returns true if exactly one bit is set in `x`, false otherwise.
128
 *
129
 * \threadsafety It is safe to call this function from any thread.
130
 *
131
 * \since This function is available since SDL 3.2.0.
132
 */
133
SDL_FORCE_INLINE bool SDL_HasExactlyOneBitSet32(Uint32 x)
134
0
{
135
0
    if (x && !(x & (x - 1))) {
136
0
        return true;
137
0
    }
138
0
    return false;
139
0
}
Unexecuted instantiation: emulator.cpp:_ZL25SDL_HasExactlyOneBitSet32j
Unexecuted instantiation: sound.cpp:_ZL25SDL_HasExactlyOneBitSet32j
Unexecuted instantiation: ay8912.cpp:_ZL25SDL_HasExactlyOneBitSet32j
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL25SDL_HasExactlyOneBitSet32j
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL25SDL_HasExactlyOneBitSet32j
140
141
/* Ends C function definitions when using C++ */
142
#ifdef __cplusplus
143
}
144
#endif
145
#include <SDL3/SDL_close_code.h>
146
147
#endif /* SDL_bits_h_ */
/opt/homebrew/include/SDL3/SDL_blendmode.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryBlendmode
24
 *
25
 * Blend modes decide how two colors will mix together. There are both
26
 * standard modes for basic needs and a means to create custom modes,
27
 * dictating what sort of math to do on what color components.
28
 */
29
30
#ifndef SDL_blendmode_h_
31
#define SDL_blendmode_h_
32
33
#include <SDL3/SDL_stdinc.h>
34
35
#include <SDL3/SDL_begin_code.h>
36
/* Set up for C function definitions, even when using C++ */
37
#ifdef __cplusplus
38
extern "C" {
39
#endif
40
41
/**
42
 * A set of blend modes used in drawing operations.
43
 *
44
 * These predefined blend modes are supported everywhere.
45
 *
46
 * Additional values may be obtained from SDL_ComposeCustomBlendMode.
47
 *
48
 * \since This datatype is available since SDL 3.2.0.
49
 *
50
 * \sa SDL_ComposeCustomBlendMode
51
 */
52
typedef Uint32 SDL_BlendMode;
53
54
#define SDL_BLENDMODE_NONE                  0x00000000u /**< no blending: dstRGBA = srcRGBA */
55
1
#define SDL_BLENDMODE_BLEND                 0x00000001u /**< alpha blending: dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA)), dstA = srcA + (dstA * (1-srcA)) */
56
#define SDL_BLENDMODE_BLEND_PREMULTIPLIED   0x00000010u /**< pre-multiplied alpha blending: dstRGBA = srcRGBA + (dstRGBA * (1-srcA)) */
57
#define SDL_BLENDMODE_ADD                   0x00000002u /**< additive blending: dstRGB = (srcRGB * srcA) + dstRGB, dstA = dstA */
58
#define SDL_BLENDMODE_ADD_PREMULTIPLIED     0x00000020u /**< pre-multiplied additive blending: dstRGB = srcRGB + dstRGB, dstA = dstA */
59
#define SDL_BLENDMODE_MOD                   0x00000004u /**< color modulate: dstRGB = srcRGB * dstRGB, dstA = dstA */
60
#define SDL_BLENDMODE_MUL                   0x00000008u /**< color multiply: dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA)), dstA = dstA */
61
#define SDL_BLENDMODE_INVALID               0x7FFFFFFFu
62
63
/**
64
 * The blend operation used when combining source and destination pixel
65
 * components.
66
 *
67
 * \since This enum is available since SDL 3.2.0.
68
 */
69
typedef enum SDL_BlendOperation
70
{
71
    SDL_BLENDOPERATION_ADD              = 0x1,  /**< dst + src: supported by all renderers */
72
    SDL_BLENDOPERATION_SUBTRACT         = 0x2,  /**< src - dst : supported by D3D, OpenGL, OpenGLES, and Vulkan */
73
    SDL_BLENDOPERATION_REV_SUBTRACT     = 0x3,  /**< dst - src : supported by D3D, OpenGL, OpenGLES, and Vulkan */
74
    SDL_BLENDOPERATION_MINIMUM          = 0x4,  /**< min(dst, src) : supported by D3D, OpenGL, OpenGLES, and Vulkan */
75
    SDL_BLENDOPERATION_MAXIMUM          = 0x5   /**< max(dst, src) : supported by D3D, OpenGL, OpenGLES, and Vulkan */
76
} SDL_BlendOperation;
77
78
/**
79
 * The normalized factor used to multiply pixel components.
80
 *
81
 * The blend factors are multiplied with the pixels from a drawing operation
82
 * (src) and the pixels from the render target (dst) before the blend
83
 * operation. The comma-separated factors listed above are always applied in
84
 * the component order red, green, blue, and alpha.
85
 *
86
 * \since This enum is available since SDL 3.2.0.
87
 */
88
typedef enum SDL_BlendFactor
89
{
90
    SDL_BLENDFACTOR_ZERO                = 0x1,  /**< 0, 0, 0, 0 */
91
    SDL_BLENDFACTOR_ONE                 = 0x2,  /**< 1, 1, 1, 1 */
92
    SDL_BLENDFACTOR_SRC_COLOR           = 0x3,  /**< srcR, srcG, srcB, srcA */
93
    SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR = 0x4,  /**< 1-srcR, 1-srcG, 1-srcB, 1-srcA */
94
    SDL_BLENDFACTOR_SRC_ALPHA           = 0x5,  /**< srcA, srcA, srcA, srcA */
95
    SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA = 0x6,  /**< 1-srcA, 1-srcA, 1-srcA, 1-srcA */
96
    SDL_BLENDFACTOR_DST_COLOR           = 0x7,  /**< dstR, dstG, dstB, dstA */
97
    SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR = 0x8,  /**< 1-dstR, 1-dstG, 1-dstB, 1-dstA */
98
    SDL_BLENDFACTOR_DST_ALPHA           = 0x9,  /**< dstA, dstA, dstA, dstA */
99
    SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA = 0xA   /**< 1-dstA, 1-dstA, 1-dstA, 1-dstA */
100
} SDL_BlendFactor;
101
102
/**
103
 * Compose a custom blend mode for renderers.
104
 *
105
 * The functions SDL_SetRenderDrawBlendMode and SDL_SetTextureBlendMode accept
106
 * the SDL_BlendMode returned by this function if the renderer supports it.
107
 *
108
 * A blend mode controls how the pixels from a drawing operation (source) get
109
 * combined with the pixels from the render target (destination). First, the
110
 * components of the source and destination pixels get multiplied with their
111
 * blend factors. Then, the blend operation takes the two products and
112
 * calculates the result that will get stored in the render target.
113
 *
114
 * Expressed in pseudocode, it would look like this:
115
 *
116
 * ```c
117
 * dstRGB = colorOperation(srcRGB * srcColorFactor, dstRGB * dstColorFactor);
118
 * dstA = alphaOperation(srcA * srcAlphaFactor, dstA * dstAlphaFactor);
119
 * ```
120
 *
121
 * Where the functions `colorOperation(src, dst)` and `alphaOperation(src,
122
 * dst)` can return one of the following:
123
 *
124
 * - `src + dst`
125
 * - `src - dst`
126
 * - `dst - src`
127
 * - `min(src, dst)`
128
 * - `max(src, dst)`
129
 *
130
 * The red, green, and blue components are always multiplied with the first,
131
 * second, and third components of the SDL_BlendFactor, respectively. The
132
 * fourth component is not used.
133
 *
134
 * The alpha component is always multiplied with the fourth component of the
135
 * SDL_BlendFactor. The other components are not used in the alpha
136
 * calculation.
137
 *
138
 * Support for these blend modes varies for each renderer. To check if a
139
 * specific SDL_BlendMode is supported, create a renderer and pass it to
140
 * either SDL_SetRenderDrawBlendMode or SDL_SetTextureBlendMode. They will
141
 * return with an error if the blend mode is not supported.
142
 *
143
 * This list describes the support of custom blend modes for each renderer.
144
 * All renderers support the four blend modes listed in the SDL_BlendMode
145
 * enumeration.
146
 *
147
 * - **direct3d**: Supports all operations with all factors. However, some
148
 *   factors produce unexpected results with `SDL_BLENDOPERATION_MINIMUM` and
149
 *   `SDL_BLENDOPERATION_MAXIMUM`.
150
 * - **direct3d11**: Same as Direct3D 9.
151
 * - **opengl**: Supports the `SDL_BLENDOPERATION_ADD` operation with all
152
 *   factors. OpenGL versions 1.1, 1.2, and 1.3 do not work correctly here.
153
 * - **opengles2**: Supports the `SDL_BLENDOPERATION_ADD`,
154
 *   `SDL_BLENDOPERATION_SUBTRACT`, `SDL_BLENDOPERATION_REV_SUBTRACT`
155
 *   operations with all factors.
156
 * - **psp**: No custom blend mode support.
157
 * - **software**: No custom blend mode support.
158
 *
159
 * Some renderers do not provide an alpha component for the default render
160
 * target. The `SDL_BLENDFACTOR_DST_ALPHA` and
161
 * `SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA` factors do not have an effect in this
162
 * case.
163
 *
164
 * \param srcColorFactor the SDL_BlendFactor applied to the red, green, and
165
 *                       blue components of the source pixels.
166
 * \param dstColorFactor the SDL_BlendFactor applied to the red, green, and
167
 *                       blue components of the destination pixels.
168
 * \param colorOperation the SDL_BlendOperation used to combine the red,
169
 *                       green, and blue components of the source and
170
 *                       destination pixels.
171
 * \param srcAlphaFactor the SDL_BlendFactor applied to the alpha component of
172
 *                       the source pixels.
173
 * \param dstAlphaFactor the SDL_BlendFactor applied to the alpha component of
174
 *                       the destination pixels.
175
 * \param alphaOperation the SDL_BlendOperation used to combine the alpha
176
 *                       component of the source and destination pixels.
177
 * \returns an SDL_BlendMode that represents the chosen factors and
178
 *          operations.
179
 *
180
 * \threadsafety It is safe to call this function from any thread.
181
 *
182
 * \since This function is available since SDL 3.2.0.
183
 *
184
 * \sa SDL_SetRenderDrawBlendMode
185
 * \sa SDL_GetRenderDrawBlendMode
186
 * \sa SDL_SetTextureBlendMode
187
 * \sa SDL_GetTextureBlendMode
188
 */
189
extern SDL_DECLSPEC SDL_BlendMode SDLCALL SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor,
190
                                                                 SDL_BlendFactor dstColorFactor,
191
                                                                 SDL_BlendOperation colorOperation,
192
                                                                 SDL_BlendFactor srcAlphaFactor,
193
                                                                 SDL_BlendFactor dstAlphaFactor,
194
                                                                 SDL_BlendOperation alphaOperation);
195
196
/* Ends C function definitions when using C++ */
197
#ifdef __cplusplus
198
}
199
#endif
200
#include <SDL3/SDL_close_code.h>
201
202
#endif /* SDL_blendmode_h_ */
/opt/homebrew/include/SDL3/SDL_endian.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryEndian
24
 *
25
 * Functions converting endian-specific values to different byte orders.
26
 *
27
 * These functions either unconditionally swap byte order (SDL_Swap16,
28
 * SDL_Swap32, SDL_Swap64, SDL_SwapFloat), or they swap to/from the system's
29
 * native byte order (SDL_Swap16LE, SDL_Swap16BE, SDL_Swap32LE, SDL_Swap32BE,
30
 * SDL_Swap32LE, SDL_Swap32BE, SDL_SwapFloatLE, SDL_SwapFloatBE). In the
31
 * latter case, the functionality is provided by macros that become no-ops if
32
 * a swap isn't necessary: on an x86 (littleendian) processor, SDL_Swap32LE
33
 * does nothing, but SDL_Swap32BE reverses the bytes of the data. On a PowerPC
34
 * processor (bigendian), the macros behavior is reversed.
35
 *
36
 * The swap routines are inline functions, and attempt to use compiler
37
 * intrinsics, inline assembly, and other magic to make byteswapping
38
 * efficient.
39
 */
40
41
#ifndef SDL_endian_h_
42
#define SDL_endian_h_
43
44
#include <SDL3/SDL_stdinc.h>
45
46
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
47
/* As of Clang 11, '_m_prefetchw' is conflicting with the winnt.h's version,
48
   so we define the needed '_m_prefetch' here as a pseudo-header, until the issue is fixed. */
49
#if defined(__clang__) &&  !SDL_HAS_BUILTIN(_m_prefetch)
50
#ifndef __PRFCHWINTRIN_H
51
#define __PRFCHWINTRIN_H
52
static __inline__ void __attribute__((__always_inline__, __nodebug__))
53
_m_prefetch(void *__P)
54
{
55
  __builtin_prefetch(__P, 0, 3 /* _MM_HINT_T0 */);
56
}
57
#endif /* __PRFCHWINTRIN_H */
58
#endif /* __clang__ */
59
60
#include <intrin.h>
61
#endif
62
63
/**
64
 *  \name The two types of endianness
65
 */
66
/* @{ */
67
68
69
/**
70
 * A value to represent littleendian byteorder.
71
 *
72
 * This is used with the preprocessor macro SDL_BYTEORDER, to determine a
73
 * platform's byte ordering:
74
 *
75
 * ```c
76
 * #if SDL_BYTEORDER == SDL_LIL_ENDIAN
77
 * SDL_Log("This system is littleendian.");
78
 * #endif
79
 * ```
80
 *
81
 * \since This macro is available since SDL 3.2.0.
82
 *
83
 * \sa SDL_BYTEORDER
84
 * \sa SDL_BIG_ENDIAN
85
 */
86
#define SDL_LIL_ENDIAN  1234
87
88
/**
89
 * A value to represent bigendian byteorder.
90
 *
91
 * This is used with the preprocessor macro SDL_BYTEORDER, to determine a
92
 * platform's byte ordering:
93
 *
94
 * ```c
95
 * #if SDL_BYTEORDER == SDL_BIG_ENDIAN
96
 * SDL_Log("This system is bigendian.");
97
 * #endif
98
 * ```
99
 *
100
 * \since This macro is available since SDL 3.2.0.
101
 *
102
 * \sa SDL_BYTEORDER
103
 * \sa SDL_LIL_ENDIAN
104
 */
105
#define SDL_BIG_ENDIAN  4321
106
107
/* @} */
108
109
#ifndef SDL_BYTEORDER
110
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
111
112
/**
113
 * A macro that reports the target system's byte order.
114
 *
115
 * This is set to either SDL_LIL_ENDIAN or SDL_BIG_ENDIAN (and maybe other
116
 * values in the future, if something else becomes popular). This can be
117
 * tested with the preprocessor, so decisions can be made at compile time.
118
 *
119
 * ```c
120
 * #if SDL_BYTEORDER == SDL_BIG_ENDIAN
121
 * SDL_Log("This system is bigendian.");
122
 * #endif
123
 * ```
124
 *
125
 * \since This macro is available since SDL 3.2.0.
126
 *
127
 * \sa SDL_LIL_ENDIAN
128
 * \sa SDL_BIG_ENDIAN
129
 */
130
#define SDL_BYTEORDER   SDL_LIL_ENDIAN___or_maybe___SDL_BIG_ENDIAN
131
#elif defined(SDL_PLATFORM_LINUX) || defined(__GLIBC__)
132
#include <endian.h>
133
#define SDL_BYTEORDER  __BYTE_ORDER
134
#elif defined(SDL_PLATFORM_SOLARIS)
135
#include <sys/byteorder.h>
136
#if defined(_LITTLE_ENDIAN)
137
#define SDL_BYTEORDER   SDL_LIL_ENDIAN
138
#elif defined(_BIG_ENDIAN)
139
#define SDL_BYTEORDER   SDL_BIG_ENDIAN
140
#else
141
#error Unsupported endianness
142
#endif
143
#elif defined(SDL_PLATFORM_OPENBSD) || defined(__DragonFly__)
144
#include <endian.h>
145
#define SDL_BYTEORDER  BYTE_ORDER
146
#elif defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_NETBSD)
147
#include <sys/endian.h>
148
#define SDL_BYTEORDER  BYTE_ORDER
149
/* predefs from newer gcc and clang versions: */
150
#elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__BYTE_ORDER__)
151
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
152
#define SDL_BYTEORDER   SDL_LIL_ENDIAN
153
#elif (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
154
#define SDL_BYTEORDER   SDL_BIG_ENDIAN
155
#else
156
#error Unsupported endianness
157
#endif /**/
158
#else
159
#if defined(__hppa__) || \
160
    defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
161
    (defined(__MIPS__) && defined(__MIPSEB__)) || \
162
    defined(__ppc__) || defined(__POWERPC__) || defined(__powerpc__) || defined(__PPC__) || \
163
    defined(__sparc__) || defined(__sparc)
164
#define SDL_BYTEORDER   SDL_BIG_ENDIAN
165
#else
166
#define SDL_BYTEORDER   SDL_LIL_ENDIAN
167
#endif
168
#endif /* SDL_PLATFORM_LINUX */
169
#endif /* !SDL_BYTEORDER */
170
171
#ifndef SDL_FLOATWORDORDER
172
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
173
174
/**
175
 * A macro that reports the target system's floating point word order.
176
 *
177
 * This is set to either SDL_LIL_ENDIAN or SDL_BIG_ENDIAN (and maybe other
178
 * values in the future, if something else becomes popular). This can be
179
 * tested with the preprocessor, so decisions can be made at compile time.
180
 *
181
 * ```c
182
 * #if SDL_FLOATWORDORDER == SDL_BIG_ENDIAN
183
 * SDL_Log("This system's floats are bigendian.");
184
 * #endif
185
 * ```
186
 *
187
 * \since This macro is available since SDL 3.2.0.
188
 *
189
 * \sa SDL_LIL_ENDIAN
190
 * \sa SDL_BIG_ENDIAN
191
 */
192
#define SDL_FLOATWORDORDER   SDL_LIL_ENDIAN___or_maybe___SDL_BIG_ENDIAN
193
/* predefs from newer gcc versions: */
194
#elif defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__FLOAT_WORD_ORDER__)
195
#if (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__)
196
#define SDL_FLOATWORDORDER   SDL_LIL_ENDIAN
197
#elif (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__)
198
#define SDL_FLOATWORDORDER   SDL_BIG_ENDIAN
199
#else
200
#error Unsupported endianness
201
#endif /**/
202
#elif defined(__MAVERICK__)
203
/* For Maverick, float words are always little-endian. */
204
#define SDL_FLOATWORDORDER   SDL_LIL_ENDIAN
205
#elif (defined(__arm__) || defined(__thumb__)) && !defined(__VFP_FP__) && !defined(__ARM_EABI__)
206
/* For FPA, float words are always big-endian. */
207
#define SDL_FLOATWORDORDER   SDL_BIG_ENDIAN
208
#else
209
/* By default, assume that floats words follow the memory system mode. */
210
#define SDL_FLOATWORDORDER   SDL_BYTEORDER
211
#endif /* __FLOAT_WORD_ORDER__ */
212
#endif /* !SDL_FLOATWORDORDER */
213
214
215
#include <SDL3/SDL_begin_code.h>
216
/* Set up for C function definitions, even when using C++ */
217
#ifdef __cplusplus
218
extern "C" {
219
#endif
220
221
/* various modern compilers may have builtin swap */
222
#if defined(__GNUC__) || defined(__clang__)
223
#   define HAS_BUILTIN_BSWAP16 (SDL_HAS_BUILTIN(__builtin_bswap16)) || \
224
        (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
225
#   define HAS_BUILTIN_BSWAP32 (SDL_HAS_BUILTIN(__builtin_bswap32)) || \
226
        (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
227
#   define HAS_BUILTIN_BSWAP64 (SDL_HAS_BUILTIN(__builtin_bswap64)) || \
228
        (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
229
230
    /* this one is broken */
231
#   define HAS_BROKEN_BSWAP (__GNUC__ == 2 && __GNUC_MINOR__ <= 95)
232
#else
233
#   define HAS_BUILTIN_BSWAP16 0
234
#   define HAS_BUILTIN_BSWAP32 0
235
#   define HAS_BUILTIN_BSWAP64 0
236
#   define HAS_BROKEN_BSWAP 0
237
#endif
238
239
/* Byte swap 16-bit integer. */
240
#ifndef SDL_WIKI_DOCUMENTATION_SECTION
241
#if HAS_BUILTIN_BSWAP16
242
#define SDL_Swap16(x) __builtin_bswap16(x)
243
#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL)
244
#pragma intrinsic(_byteswap_ushort)
245
#define SDL_Swap16(x) _byteswap_ushort(x)
246
#elif defined(__i386__) && !HAS_BROKEN_BSWAP
247
SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x)
248
{
249
  __asm__("xchgb %b0,%h0": "=q"(x):"0"(x));
250
    return x;
251
}
252
#elif defined(__x86_64__)
253
SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x)
254
{
255
  __asm__("xchgb %b0,%h0": "=Q"(x):"0"(x));
256
    return x;
257
}
258
#elif (defined(__powerpc__) || defined(__ppc__))
259
SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x)
260
{
261
    int result;
262
263
  __asm__("rlwimi %0,%2,8,16,23": "=&r"(result):"0"(x >> 8), "r"(x));
264
    return (Uint16)result;
265
}
266
#elif (defined(__m68k__) && !defined(__mcoldfire__))
267
SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x)
268
{
269
  __asm__("rorw #8,%0": "=d"(x): "0"(x):"cc");
270
    return x;
271
}
272
#elif defined(__WATCOMC__) && defined(__386__)
273
extern __inline Uint16 SDL_Swap16(Uint16);
274
#pragma aux SDL_Swap16 = \
275
  "xchg al, ah" \
276
  parm   [ax]   \
277
  modify [ax];
278
#else
279
SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x)
280
{
281
    return SDL_static_cast(Uint16, ((x << 8) | (x >> 8)));
282
}
283
#endif
284
#endif
285
286
/* Byte swap 32-bit integer. */
287
#ifndef SDL_WIKI_DOCUMENTATION_SECTION
288
#if HAS_BUILTIN_BSWAP32
289
#define SDL_Swap32(x) __builtin_bswap32(x)
290
#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL)
291
#pragma intrinsic(_byteswap_ulong)
292
#define SDL_Swap32(x) _byteswap_ulong(x)
293
#elif defined(__i386__) && !HAS_BROKEN_BSWAP
294
SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x)
295
{
296
  __asm__("bswap %0": "=r"(x):"0"(x));
297
    return x;
298
}
299
#elif defined(__x86_64__)
300
SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x)
301
{
302
  __asm__("bswapl %0": "=r"(x):"0"(x));
303
    return x;
304
}
305
#elif (defined(__powerpc__) || defined(__ppc__))
306
SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x)
307
{
308
    Uint32 result;
309
310
  __asm__("rlwimi %0,%2,24,16,23": "=&r"(result): "0" (x>>24),  "r"(x));
311
  __asm__("rlwimi %0,%2,8,8,15"  : "=&r"(result): "0" (result), "r"(x));
312
  __asm__("rlwimi %0,%2,24,0,7"  : "=&r"(result): "0" (result), "r"(x));
313
    return result;
314
}
315
#elif (defined(__m68k__) && !defined(__mcoldfire__))
316
SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x)
317
{
318
  __asm__("rorw #8,%0\n\tswap %0\n\trorw #8,%0": "=d"(x): "0"(x):"cc");
319
    return x;
320
}
321
#elif defined(__WATCOMC__) && defined(__386__)
322
extern __inline Uint32 SDL_Swap32(Uint32);
323
#pragma aux SDL_Swap32 = \
324
  "bswap eax"  \
325
  parm   [eax] \
326
  modify [eax];
327
#else
328
SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x)
329
{
330
    return SDL_static_cast(Uint32, ((x << 24) | ((x << 8) & 0x00FF0000) |
331
                                    ((x >> 8) & 0x0000FF00) | (x >> 24)));
332
}
333
#endif
334
#endif
335
336
/* Byte swap 64-bit integer. */
337
#ifndef SDL_WIKI_DOCUMENTATION_SECTION
338
#if HAS_BUILTIN_BSWAP64
339
#define SDL_Swap64(x) __builtin_bswap64(x)
340
#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && !defined(__ICL)
341
#pragma intrinsic(_byteswap_uint64)
342
#define SDL_Swap64(x) _byteswap_uint64(x)
343
#elif defined(__i386__) && !HAS_BROKEN_BSWAP
344
SDL_FORCE_INLINE Uint64 SDL_Swap64(Uint64 x)
345
{
346
    union {
347
        struct {
348
            Uint32 a, b;
349
        } s;
350
        Uint64 u;
351
    } v;
352
    v.u = x;
353
  __asm__("bswapl %0 ; bswapl %1 ; xchgl %0,%1"
354
          : "=r"(v.s.a), "=r"(v.s.b)
355
          : "0" (v.s.a),  "1"(v.s.b));
356
    return v.u;
357
}
358
#elif defined(__x86_64__)
359
SDL_FORCE_INLINE Uint64 SDL_Swap64(Uint64 x)
360
{
361
  __asm__("bswapq %0": "=r"(x):"0"(x));
362
    return x;
363
}
364
#elif defined(__WATCOMC__) && defined(__386__)
365
extern __inline Uint64 SDL_Swap64(Uint64);
366
#pragma aux SDL_Swap64 = \
367
  "bswap eax"     \
368
  "bswap edx"     \
369
  "xchg eax,edx"  \
370
  parm [eax edx]  \
371
  modify [eax edx];
372
#else
373
SDL_FORCE_INLINE Uint64 SDL_Swap64(Uint64 x)
374
{
375
    Uint32 hi, lo;
376
377
    /* Separate into high and low 32-bit values and swap them */
378
    lo = SDL_static_cast(Uint32, x & 0xFFFFFFFF);
379
    x >>= 32;
380
    hi = SDL_static_cast(Uint32, x & 0xFFFFFFFF);
381
    x = SDL_Swap32(lo);
382
    x <<= 32;
383
    x |= SDL_Swap32(hi);
384
    return (x);
385
}
386
#endif
387
#endif
388
389
/**
390
 * Byte-swap a floating point number.
391
 *
392
 * This will always byte-swap the value, whether it's currently in the native
393
 * byteorder of the system or not. You should use SDL_SwapFloatLE or
394
 * SDL_SwapFloatBE instead, in most cases.
395
 *
396
 * Note that this is a forced-inline function in a header, and not a public
397
 * API function available in the SDL library (which is to say, the code is
398
 * embedded in the calling program and the linker and dynamic loader will not
399
 * be able to find this function inside SDL itself).
400
 *
401
 * \param x the value to byte-swap.
402
 * \returns x, with its bytes in the opposite endian order.
403
 *
404
 * \threadsafety It is safe to call this function from any thread.
405
 *
406
 * \since This function is available since SDL 3.2.0.
407
 */
408
SDL_FORCE_INLINE float SDL_SwapFloat(float x)
409
0
{
410
0
    union {
411
0
        float f;
412
0
        Uint32 ui32;
413
0
    } swapper;
414
0
    swapper.f = x;
415
0
    swapper.ui32 = SDL_Swap32(swapper.ui32);
416
0
    return swapper.f;
417
0
}
Unexecuted instantiation: emulator.cpp:_ZL13SDL_SwapFloatf
Unexecuted instantiation: sound.cpp:_ZL13SDL_SwapFloatf
Unexecuted instantiation: ay8912.cpp:_ZL13SDL_SwapFloatf
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL13SDL_SwapFloatf
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL13SDL_SwapFloatf
418
419
/* remove extra macros */
420
#undef HAS_BROKEN_BSWAP
421
#undef HAS_BUILTIN_BSWAP16
422
#undef HAS_BUILTIN_BSWAP32
423
#undef HAS_BUILTIN_BSWAP64
424
425
426
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
427
428
/**
429
 * Byte-swap an unsigned 16-bit number.
430
 *
431
 * This will always byte-swap the value, whether it's currently in the native
432
 * byteorder of the system or not. You should use SDL_Swap16LE or SDL_Swap16BE
433
 * instead, in most cases.
434
 *
435
 * Note that this is a forced-inline function in a header, and not a public
436
 * API function available in the SDL library (which is to say, the code is
437
 * embedded in the calling program and the linker and dynamic loader will not
438
 * be able to find this function inside SDL itself).
439
 *
440
 * \param x the value to byte-swap.
441
 * \returns `x`, with its bytes in the opposite endian order.
442
 *
443
 * \threadsafety It is safe to call this function from any thread.
444
 *
445
 * \since This function is available since SDL 3.2.0.
446
 */
447
SDL_FORCE_INLINE Uint16 SDL_Swap16(Uint16 x) { return x_but_byteswapped; }
448
449
/**
450
 * Byte-swap an unsigned 32-bit number.
451
 *
452
 * This will always byte-swap the value, whether it's currently in the native
453
 * byteorder of the system or not. You should use SDL_Swap32LE or SDL_Swap32BE
454
 * instead, in most cases.
455
 *
456
 * Note that this is a forced-inline function in a header, and not a public
457
 * API function available in the SDL library (which is to say, the code is
458
 * embedded in the calling program and the linker and dynamic loader will not
459
 * be able to find this function inside SDL itself).
460
 *
461
 * \param x the value to byte-swap.
462
 * \returns `x`, with its bytes in the opposite endian order.
463
 *
464
 * \threadsafety It is safe to call this function from any thread.
465
 *
466
 * \since This function is available since SDL 3.2.0.
467
 */
468
SDL_FORCE_INLINE Uint32 SDL_Swap32(Uint32 x) { return x_but_byteswapped; }
469
470
/**
471
 * Byte-swap an unsigned 64-bit number.
472
 *
473
 * This will always byte-swap the value, whether it's currently in the native
474
 * byteorder of the system or not. You should use SDL_Swap64LE or SDL_Swap64BE
475
 * instead, in most cases.
476
 *
477
 * Note that this is a forced-inline function in a header, and not a public
478
 * API function available in the SDL library (which is to say, the code is
479
 * embedded in the calling program and the linker and dynamic loader will not
480
 * be able to find this function inside SDL itself).
481
 *
482
 * \param x the value to byte-swap.
483
 * \returns `x`, with its bytes in the opposite endian order.
484
 *
485
 * \threadsafety It is safe to call this function from any thread.
486
 *
487
 * \since This function is available since SDL 3.2.0.
488
 */
489
SDL_FORCE_INLINE Uint64 SDL_Swap64(Uint64 x) { return x_but_byteswapped; }
490
491
/**
492
 * Swap a 16-bit value from littleendian to native byte order.
493
 *
494
 * If this is running on a littleendian system, `x` is returned unchanged.
495
 *
496
 * This macro never references `x` more than once, avoiding side effects.
497
 *
498
 * \param x the value to swap, in littleendian byte order.
499
 * \returns `x` in native byte order.
500
 *
501
 * \threadsafety It is safe to call this macro from any thread.
502
 *
503
 * \since This macro is available since SDL 3.2.0.
504
 */
505
#define SDL_Swap16LE(x) SwapOnlyIfNecessary(x)
506
507
/**
508
 * Swap a 32-bit value from littleendian to native byte order.
509
 *
510
 * If this is running on a littleendian system, `x` is returned unchanged.
511
 *
512
 * This macro never references `x` more than once, avoiding side effects.
513
 *
514
 * \param x the value to swap, in littleendian byte order.
515
 * \returns `x` in native byte order.
516
 *
517
 * \threadsafety It is safe to call this macro from any thread.
518
 *
519
 * \since This macro is available since SDL 3.2.0.
520
 */
521
#define SDL_Swap32LE(x) SwapOnlyIfNecessary(x)
522
523
/**
524
 * Swap a 64-bit value from littleendian to native byte order.
525
 *
526
 * If this is running on a littleendian system, `x` is returned unchanged.
527
 *
528
 * This macro never references `x` more than once, avoiding side effects.
529
 *
530
 * \param x the value to swap, in littleendian byte order.
531
 * \returns `x` in native byte order.
532
 *
533
 * \threadsafety It is safe to call this macro from any thread.
534
 *
535
 * \since This macro is available since SDL 3.2.0.
536
 */
537
#define SDL_Swap64LE(x) SwapOnlyIfNecessary(x)
538
539
/**
540
 * Swap a floating point value from littleendian to native byte order.
541
 *
542
 * If this is running on a littleendian system, `x` is returned unchanged.
543
 *
544
 * This macro never references `x` more than once, avoiding side effects.
545
 *
546
 * \param x the value to swap, in littleendian byte order.
547
 * \returns `x` in native byte order.
548
 *
549
 * \threadsafety It is safe to call this macro from any thread.
550
 *
551
 * \since This macro is available since SDL 3.2.0.
552
 */
553
#define SDL_SwapFloatLE(x) SwapOnlyIfNecessary(x)
554
555
/**
556
 * Swap a 16-bit value from bigendian to native byte order.
557
 *
558
 * If this is running on a bigendian system, `x` is returned unchanged.
559
 *
560
 * This macro never references `x` more than once, avoiding side effects.
561
 *
562
 * \param x the value to swap, in bigendian byte order.
563
 * \returns `x` in native byte order.
564
 *
565
 * \threadsafety It is safe to call this macro from any thread.
566
 *
567
 * \since This macro is available since SDL 3.2.0.
568
 */
569
#define SDL_Swap16BE(x) SwapOnlyIfNecessary(x)
570
571
/**
572
 * Swap a 32-bit value from bigendian to native byte order.
573
 *
574
 * If this is running on a bigendian system, `x` is returned unchanged.
575
 *
576
 * This macro never references `x` more than once, avoiding side effects.
577
 *
578
 * \param x the value to swap, in bigendian byte order.
579
 * \returns `x` in native byte order.
580
 *
581
 * \threadsafety It is safe to call this macro from any thread.
582
 *
583
 * \since This macro is available since SDL 3.2.0.
584
 */
585
#define SDL_Swap32BE(x) SwapOnlyIfNecessary(x)
586
587
/**
588
 * Swap a 64-bit value from bigendian to native byte order.
589
 *
590
 * If this is running on a bigendian system, `x` is returned unchanged.
591
 *
592
 * This macro never references `x` more than once, avoiding side effects.
593
 *
594
 * \param x the value to swap, in bigendian byte order.
595
 * \returns `x` in native byte order.
596
 *
597
 * \threadsafety It is safe to call this macro from any thread.
598
 *
599
 * \since This macro is available since SDL 3.2.0.
600
 */
601
#define SDL_Swap64BE(x) SwapOnlyIfNecessary(x)
602
603
/**
604
 * Swap a floating point value from bigendian to native byte order.
605
 *
606
 * If this is running on a bigendian system, `x` is returned unchanged.
607
 *
608
 * This macro never references `x` more than once, avoiding side effects.
609
 *
610
 * \param x the value to swap, in bigendian byte order.
611
 * \returns `x` in native byte order.
612
 *
613
 * \threadsafety It is safe to call this macro from any thread.
614
 *
615
 * \since This macro is available since SDL 3.2.0.
616
 */
617
#define SDL_SwapFloatBE(x) SwapOnlyIfNecessary(x)
618
619
#elif SDL_BYTEORDER == SDL_LIL_ENDIAN
620
#define SDL_Swap16LE(x)     (x)
621
#define SDL_Swap32LE(x)     (x)
622
#define SDL_Swap64LE(x)     (x)
623
#define SDL_SwapFloatLE(x)  (x)
624
#define SDL_Swap16BE(x)     SDL_Swap16(x)
625
#define SDL_Swap32BE(x)     SDL_Swap32(x)
626
#define SDL_Swap64BE(x)     SDL_Swap64(x)
627
#define SDL_SwapFloatBE(x)  SDL_SwapFloat(x)
628
#else
629
#define SDL_Swap16LE(x)     SDL_Swap16(x)
630
#define SDL_Swap32LE(x)     SDL_Swap32(x)
631
#define SDL_Swap64LE(x)     SDL_Swap64(x)
632
#define SDL_SwapFloatLE(x)  SDL_SwapFloat(x)
633
#define SDL_Swap16BE(x)     (x)
634
#define SDL_Swap32BE(x)     (x)
635
#define SDL_Swap64BE(x)     (x)
636
#define SDL_SwapFloatBE(x)  (x)
637
#endif
638
639
/* Ends C function definitions when using C++ */
640
#ifdef __cplusplus
641
}
642
#endif
643
#include <SDL3/SDL_close_code.h>
644
645
#endif /* SDL_endian_h_ */
/opt/homebrew/include/SDL3/SDL_hints.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryHints
24
 *
25
 * This file contains functions to set and get configuration hints, as well as
26
 * listing each of them alphabetically.
27
 *
28
 * The convention for naming hints is SDL_HINT_X, where "SDL_X" is the
29
 * environment variable that can be used to override the default.
30
 *
31
 * In general these hints are just that - they may or may not be supported or
32
 * applicable on any given platform, but they provide a way for an application
33
 * or user to give the library a hint as to how they would like the library to
34
 * work.
35
 */
36
37
#ifndef SDL_hints_h_
38
#define SDL_hints_h_
39
40
#include <SDL3/SDL_error.h>
41
#include <SDL3/SDL_stdinc.h>
42
43
#include <SDL3/SDL_begin_code.h>
44
/* Set up for C function definitions, even when using C++ */
45
#ifdef __cplusplus
46
extern "C" {
47
#endif
48
49
/**
50
 * Specify the behavior of Alt+Tab while the keyboard is grabbed.
51
 *
52
 * By default, SDL emulates Alt+Tab functionality while the keyboard is
53
 * grabbed and your window is full-screen. This prevents the user from getting
54
 * stuck in your application if you've enabled keyboard grab.
55
 *
56
 * The variable can be set to the following values:
57
 *
58
 * - "0": SDL will not handle Alt+Tab. Your application is responsible for
59
 *   handling Alt+Tab while the keyboard is grabbed.
60
 * - "1": SDL will minimize your window when Alt+Tab is pressed (default)
61
 *
62
 * This hint can be set anytime.
63
 *
64
 * \since This hint is available since SDL 3.2.0.
65
 */
66
#define SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED "SDL_ALLOW_ALT_TAB_WHILE_GRABBED"
67
68
/**
69
 * A variable to control whether the SDL activity is allowed to be re-created.
70
 *
71
 * If this hint is true, the activity can be recreated on demand by the OS,
72
 * and Java static data and C++ static data remain with their current values.
73
 * If this hint is false, then SDL will call exit() when you return from your
74
 * main function and the application will be terminated and then started fresh
75
 * each time.
76
 *
77
 * The variable can be set to the following values:
78
 *
79
 * - "0": The application starts fresh at each launch. (default)
80
 * - "1": The application activity can be recreated by the OS.
81
 *
82
 * This hint can be set anytime.
83
 *
84
 * \since This hint is available since SDL 3.2.0.
85
 */
86
#define SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY "SDL_ANDROID_ALLOW_RECREATE_ACTIVITY"
87
88
/**
89
 * A variable to control whether the event loop will block itself when the app
90
 * is paused.
91
 *
92
 * The variable can be set to the following values:
93
 *
94
 * - "0": Non blocking.
95
 * - "1": Blocking. (default)
96
 *
97
 * This hint should be set before SDL is initialized.
98
 *
99
 * \since This hint is available since SDL 3.2.0.
100
 */
101
#define SDL_HINT_ANDROID_BLOCK_ON_PAUSE "SDL_ANDROID_BLOCK_ON_PAUSE"
102
103
/**
104
 * A variable to control whether low latency audio should be enabled.
105
 *
106
 * Some devices have poor quality output when this is enabled, but this is
107
 * usually an improvement in audio latency.
108
 *
109
 * The variable can be set to the following values:
110
 *
111
 * - "0": Low latency audio is not enabled.
112
 * - "1": Low latency audio is enabled. (default)
113
 *
114
 * This hint should be set before SDL audio is initialized.
115
 *
116
 * \since This hint is available since SDL 3.2.0.
117
 */
118
#define SDL_HINT_ANDROID_LOW_LATENCY_AUDIO "SDL_ANDROID_LOW_LATENCY_AUDIO"
119
120
/**
121
 * A variable to control whether we trap the Android back button to handle it
122
 * manually.
123
 *
124
 * This is necessary for the right mouse button to work on some Android
125
 * devices, or to be able to trap the back button for use in your code
126
 * reliably. If this hint is true, the back button will show up as an
127
 * SDL_EVENT_KEY_DOWN / SDL_EVENT_KEY_UP pair with a keycode of
128
 * SDL_SCANCODE_AC_BACK.
129
 *
130
 * The variable can be set to the following values:
131
 *
132
 * - "0": Back button will be handled as usual for system. (default)
133
 * - "1": Back button will be trapped, allowing you to handle the key press
134
 *   manually. (This will also let right mouse click work on systems where the
135
 *   right mouse button functions as back.)
136
 *
137
 * This hint can be set anytime.
138
 *
139
 * \since This hint is available since SDL 3.2.0.
140
 */
141
#define SDL_HINT_ANDROID_TRAP_BACK_BUTTON "SDL_ANDROID_TRAP_BACK_BUTTON"
142
143
/**
144
 * A variable setting the app ID string.
145
 *
146
 * This string is used by desktop compositors to identify and group windows
147
 * together, as well as match applications with associated desktop settings
148
 * and icons.
149
 *
150
 * This will override SDL_PROP_APP_METADATA_IDENTIFIER_STRING, if set by the
151
 * application.
152
 *
153
 * This hint should be set before SDL is initialized.
154
 *
155
 * \since This hint is available since SDL 3.2.0.
156
 */
157
#define SDL_HINT_APP_ID "SDL_APP_ID"
158
159
/**
160
 * A variable setting the application name.
161
 *
162
 * This hint lets you specify the application name sent to the OS when
163
 * required. For example, this will often appear in volume control applets for
164
 * audio streams, and in lists of applications which are inhibiting the
165
 * screensaver. You should use a string that describes your program ("My Game
166
 * 2: The Revenge")
167
 *
168
 * This will override SDL_PROP_APP_METADATA_NAME_STRING, if set by the
169
 * application.
170
 *
171
 * This hint should be set before SDL is initialized.
172
 *
173
 * \since This hint is available since SDL 3.2.0.
174
 */
175
#define SDL_HINT_APP_NAME "SDL_APP_NAME"
176
177
/**
178
 * A variable controlling whether controllers used with the Apple TV generate
179
 * UI events.
180
 *
181
 * When UI events are generated by controller input, the app will be
182
 * backgrounded when the Apple TV remote's menu button is pressed, and when
183
 * the pause or B buttons on gamepads are pressed.
184
 *
185
 * More information about properly making use of controllers for the Apple TV
186
 * can be found here:
187
 * https://developer.apple.com/tvos/human-interface-guidelines/remote-and-controllers/
188
 *
189
 * The variable can be set to the following values:
190
 *
191
 * - "0": Controller input does not generate UI events. (default)
192
 * - "1": Controller input generates UI events.
193
 *
194
 * This hint can be set anytime.
195
 *
196
 * \since This hint is available since SDL 3.2.0.
197
 */
198
#define SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS "SDL_APPLE_TV_CONTROLLER_UI_EVENTS"
199
200
/**
201
 * A variable controlling whether the Apple TV remote's joystick axes will
202
 * automatically match the rotation of the remote.
203
 *
204
 * The variable can be set to the following values:
205
 *
206
 * - "0": Remote orientation does not affect joystick axes. (default)
207
 * - "1": Joystick axes are based on the orientation of the remote.
208
 *
209
 * This hint can be set anytime.
210
 *
211
 * \since This hint is available since SDL 3.2.0.
212
 */
213
#define SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION"
214
215
/**
216
 * Specify the default ALSA audio device name.
217
 *
218
 * This variable is a specific audio device to open when the "default" audio
219
 * device is used.
220
 *
221
 * This hint will be ignored when opening the default playback device if
222
 * SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE is set, or when opening the
223
 * default recording device if SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE is
224
 * set.
225
 *
226
 * This hint should be set before an audio device is opened.
227
 *
228
 * \since This hint is available since SDL 3.2.0.
229
 *
230
 * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE
231
 * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE
232
 */
233
#define SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE "SDL_AUDIO_ALSA_DEFAULT_DEVICE"
234
235
/**
236
 * Specify the default ALSA audio playback device name.
237
 *
238
 * This variable is a specific audio device to open for playback, when the
239
 * "default" audio device is used.
240
 *
241
 * If this hint isn't set, SDL will check SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE
242
 * before choosing a reasonable default.
243
 *
244
 * This hint should be set before an audio device is opened.
245
 *
246
 * \since This hint is available since SDL 3.2.0.
247
 *
248
 * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE
249
 * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE
250
 */
251
#define SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE "SDL_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE"
252
253
/**
254
 * Specify the default ALSA audio recording device name.
255
 *
256
 * This variable is a specific audio device to open for recording, when the
257
 * "default" audio device is used.
258
 *
259
 * If this hint isn't set, SDL will check SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE
260
 * before choosing a reasonable default.
261
 *
262
 * This hint should be set before an audio device is opened.
263
 *
264
 * \since This hint is available since SDL 3.2.0.
265
 *
266
 * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE
267
 * \sa SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE
268
 */
269
#define SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE "SDL_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE"
270
271
/**
272
 * A variable controlling the audio category on iOS and macOS.
273
 *
274
 * The variable can be set to the following values:
275
 *
276
 * - "ambient": Use the AVAudioSessionCategoryAmbient audio category, will be
277
 *   muted by the phone mute switch (default)
278
 * - "playback": Use the AVAudioSessionCategoryPlayback category.
279
 *
280
 * For more information, see Apple's documentation:
281
 * https://developer.apple.com/library/content/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/AudioSessionCategoriesandModes/AudioSessionCategoriesandModes.html
282
 *
283
 * This hint should be set before an audio device is opened.
284
 *
285
 * \since This hint is available since SDL 3.2.0.
286
 */
287
#define SDL_HINT_AUDIO_CATEGORY "SDL_AUDIO_CATEGORY"
288
289
/**
290
 * A variable controlling the default audio channel count.
291
 *
292
 * If the application doesn't specify the audio channel count when opening the
293
 * device, this hint can be used to specify a default channel count that will
294
 * be used. This defaults to "1" for recording and "2" for playback devices.
295
 *
296
 * This hint should be set before an audio device is opened.
297
 *
298
 * \since This hint is available since SDL 3.2.0.
299
 */
300
#define SDL_HINT_AUDIO_CHANNELS "SDL_AUDIO_CHANNELS"
301
302
/**
303
 * Specify an application icon name for an audio device.
304
 *
305
 * Some audio backends (such as Pulseaudio and Pipewire) allow you to set an
306
 * XDG icon name for your application. Among other things, this icon might
307
 * show up in a system control panel that lets the user adjust the volume on
308
 * specific audio streams instead of using one giant master volume slider.
309
 * Note that this is unrelated to the icon used by the windowing system, which
310
 * may be set with SDL_SetWindowIcon (or via desktop file on Wayland).
311
 *
312
 * Setting this to "" or leaving it unset will have SDL use a reasonable
313
 * default, "applications-games", which is likely to be installed. See
314
 * https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
315
 * and
316
 * https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
317
 * for the relevant XDG icon specs.
318
 *
319
 * This hint should be set before an audio device is opened.
320
 *
321
 * \since This hint is available since SDL 3.2.0.
322
 */
323
#define SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME "SDL_AUDIO_DEVICE_APP_ICON_NAME"
324
325
/**
326
 * A variable controlling device buffer size.
327
 *
328
 * This hint is an integer > 0, that represents the size of the device's
329
 * buffer in sample frames (stereo audio data in 16-bit format is 4 bytes per
330
 * sample frame, for example).
331
 *
332
 * SDL3 generally decides this value on behalf of the app, but if for some
333
 * reason the app needs to dictate this (because they want either lower
334
 * latency or higher throughput AND ARE WILLING TO DEAL WITH what that might
335
 * require of the app), they can specify it.
336
 *
337
 * SDL will try to accommodate this value, but there is no promise you'll get
338
 * the buffer size requested. Many platforms won't honor this request at all,
339
 * or might adjust it.
340
 *
341
 * This hint should be set before an audio device is opened.
342
 *
343
 * \since This hint is available since SDL 3.2.0.
344
 */
345
#define SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES "SDL_AUDIO_DEVICE_SAMPLE_FRAMES"
346
347
/**
348
 * Specify an audio stream name for an audio device.
349
 *
350
 * Some audio backends (such as PulseAudio) allow you to describe your audio
351
 * stream. Among other things, this description might show up in a system
352
 * control panel that lets the user adjust the volume on specific audio
353
 * streams instead of using one giant master volume slider.
354
 *
355
 * This hints lets you transmit that information to the OS. The contents of
356
 * this hint are used while opening an audio device. You should use a string
357
 * that describes your what your program is playing ("audio stream" is
358
 * probably sufficient in many cases, but this could be useful for something
359
 * like "team chat" if you have a headset playing VoIP audio separately).
360
 *
361
 * Setting this to "" or leaving it unset will have SDL use a reasonable
362
 * default: "audio stream" or something similar.
363
 *
364
 * Note that while this talks about audio streams, this is an OS-level
365
 * concept, so it applies to a physical audio device in this case, and not an
366
 * SDL_AudioStream, nor an SDL logical audio device.
367
 *
368
 * This hint should be set before an audio device is opened.
369
 *
370
 * \since This hint is available since SDL 3.2.0.
371
 */
372
#define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME"
373
374
/**
375
 * Specify an application role for an audio device.
376
 *
377
 * Some audio backends (such as Pipewire) allow you to describe the role of
378
 * your audio stream. Among other things, this description might show up in a
379
 * system control panel or software for displaying and manipulating media
380
 * playback/recording graphs.
381
 *
382
 * This hints lets you transmit that information to the OS. The contents of
383
 * this hint are used while opening an audio device. You should use a string
384
 * that describes your what your program is playing (Game, Music, Movie,
385
 * etc...).
386
 *
387
 * Setting this to "" or leaving it unset will have SDL use a reasonable
388
 * default: "Game" or something similar.
389
 *
390
 * Note that while this talks about audio streams, this is an OS-level
391
 * concept, so it applies to a physical audio device in this case, and not an
392
 * SDL_AudioStream, nor an SDL logical audio device.
393
 *
394
 * This hint should be set before an audio device is opened.
395
 *
396
 * \since This hint is available since SDL 3.2.0.
397
 */
398
#define SDL_HINT_AUDIO_DEVICE_STREAM_ROLE "SDL_AUDIO_DEVICE_STREAM_ROLE"
399
400
/**
401
 * Specify the input file when recording audio using the disk audio driver.
402
 *
403
 * This defaults to "sdlaudio-in.raw"
404
 *
405
 * This hint should be set before an audio device is opened.
406
 *
407
 * \since This hint is available since SDL 3.2.0.
408
 */
409
#define SDL_HINT_AUDIO_DISK_INPUT_FILE "SDL_AUDIO_DISK_INPUT_FILE"
410
411
/**
412
 * Specify the output file when playing audio using the disk audio driver.
413
 *
414
 * This defaults to "sdlaudio.raw"
415
 *
416
 * This hint should be set before an audio device is opened.
417
 *
418
 * \since This hint is available since SDL 3.2.0.
419
 */
420
#define SDL_HINT_AUDIO_DISK_OUTPUT_FILE "SDL_AUDIO_DISK_OUTPUT_FILE"
421
422
/**
423
 * A variable controlling the audio rate when using the disk audio driver.
424
 *
425
 * The disk audio driver normally simulates real-time for the audio rate that
426
 * was specified, but you can use this variable to adjust this rate higher or
427
 * lower down to 0. The default value is "1.0".
428
 *
429
 * This hint should be set before an audio device is opened.
430
 *
431
 * \since This hint is available since SDL 3.2.0.
432
 */
433
#define SDL_HINT_AUDIO_DISK_TIMESCALE "SDL_AUDIO_DISK_TIMESCALE"
434
435
/**
436
 * A variable that specifies an audio backend to use.
437
 *
438
 * By default, SDL will try all available audio backends in a reasonable order
439
 * until it finds one that can work, but this hint allows the app or user to
440
 * force a specific driver, such as "pipewire" if, say, you are on PulseAudio
441
 * but want to try talking to the lower level instead.
442
 *
443
 * This hint should be set before SDL is initialized.
444
 *
445
 * \since This hint is available since SDL 3.2.0.
446
 */
447
#define SDL_HINT_AUDIO_DRIVER "SDL_AUDIO_DRIVER"
448
449
/**
450
 * A variable controlling the audio rate when using the dummy audio driver.
451
 *
452
 * The dummy audio driver normally simulates real-time for the audio rate that
453
 * was specified, but you can use this variable to adjust this rate higher or
454
 * lower down to 0. The default value is "1.0".
455
 *
456
 * This hint should be set before an audio device is opened.
457
 *
458
 * \since This hint is available since SDL 3.2.0.
459
 */
460
#define SDL_HINT_AUDIO_DUMMY_TIMESCALE "SDL_AUDIO_DUMMY_TIMESCALE"
461
462
/**
463
 * A variable controlling the default audio format.
464
 *
465
 * If the application doesn't specify the audio format when opening the
466
 * device, this hint can be used to specify a default format that will be
467
 * used.
468
 *
469
 * The variable can be set to the following values:
470
 *
471
 * - "U8": Unsigned 8-bit audio
472
 * - "S8": Signed 8-bit audio
473
 * - "S16LE": Signed 16-bit little-endian audio
474
 * - "S16BE": Signed 16-bit big-endian audio
475
 * - "S16": Signed 16-bit native-endian audio (default)
476
 * - "S32LE": Signed 32-bit little-endian audio
477
 * - "S32BE": Signed 32-bit big-endian audio
478
 * - "S32": Signed 32-bit native-endian audio
479
 * - "F32LE": Floating point little-endian audio
480
 * - "F32BE": Floating point big-endian audio
481
 * - "F32": Floating point native-endian audio
482
 *
483
 * This hint should be set before an audio device is opened.
484
 *
485
 * \since This hint is available since SDL 3.2.0.
486
 */
487
#define SDL_HINT_AUDIO_FORMAT "SDL_AUDIO_FORMAT"
488
489
/**
490
 * A variable controlling the default audio frequency.
491
 *
492
 * If the application doesn't specify the audio frequency when opening the
493
 * device, this hint can be used to specify a default frequency that will be
494
 * used. This defaults to "44100".
495
 *
496
 * This hint should be set before an audio device is opened.
497
 *
498
 * \since This hint is available since SDL 3.2.0.
499
 */
500
#define SDL_HINT_AUDIO_FREQUENCY "SDL_AUDIO_FREQUENCY"
501
502
/**
503
 * A variable that causes SDL to not ignore audio "monitors".
504
 *
505
 * This is currently only used by the PulseAudio driver.
506
 *
507
 * By default, SDL ignores audio devices that aren't associated with physical
508
 * hardware. Changing this hint to "1" will expose anything SDL sees that
509
 * appears to be an audio source or sink. This will add "devices" to the list
510
 * that the user probably doesn't want or need, but it can be useful in
511
 * scenarios where you want to hook up SDL to some sort of virtual device,
512
 * etc.
513
 *
514
 * The variable can be set to the following values:
515
 *
516
 * - "0": Audio monitor devices will be ignored. (default)
517
 * - "1": Audio monitor devices will show up in the device list.
518
 *
519
 * This hint should be set before SDL is initialized.
520
 *
521
 * \since This hint is available since SDL 3.2.0.
522
 */
523
#define SDL_HINT_AUDIO_INCLUDE_MONITORS "SDL_AUDIO_INCLUDE_MONITORS"
524
525
/**
526
 * A variable controlling whether SDL updates joystick state when getting
527
 * input events.
528
 *
529
 * The variable can be set to the following values:
530
 *
531
 * - "0": You'll call SDL_UpdateJoysticks() manually.
532
 * - "1": SDL will automatically call SDL_UpdateJoysticks(). (default)
533
 *
534
 * This hint can be set anytime.
535
 *
536
 * \since This hint is available since SDL 3.2.0.
537
 */
538
#define SDL_HINT_AUTO_UPDATE_JOYSTICKS "SDL_AUTO_UPDATE_JOYSTICKS"
539
540
/**
541
 * A variable controlling whether SDL updates sensor state when getting input
542
 * events.
543
 *
544
 * The variable can be set to the following values:
545
 *
546
 * - "0": You'll call SDL_UpdateSensors() manually.
547
 * - "1": SDL will automatically call SDL_UpdateSensors(). (default)
548
 *
549
 * This hint can be set anytime.
550
 *
551
 * \since This hint is available since SDL 3.2.0.
552
 */
553
#define SDL_HINT_AUTO_UPDATE_SENSORS "SDL_AUTO_UPDATE_SENSORS"
554
555
/**
556
 * Prevent SDL from using version 4 of the bitmap header when saving BMPs.
557
 *
558
 * The bitmap header version 4 is required for proper alpha channel support
559
 * and SDL will use it when required. Should this not be desired, this hint
560
 * can force the use of the 40 byte header version which is supported
561
 * everywhere.
562
 *
563
 * The variable can be set to the following values:
564
 *
565
 * - "0": Surfaces with a colorkey or an alpha channel are saved to a 32-bit
566
 *   BMP file with an alpha mask. SDL will use the bitmap header version 4 and
567
 *   set the alpha mask accordingly. (default)
568
 * - "1": Surfaces with a colorkey or an alpha channel are saved to a 32-bit
569
 *   BMP file without an alpha mask. The alpha channel data will be in the
570
 *   file, but applications are going to ignore it.
571
 *
572
 * This hint can be set anytime.
573
 *
574
 * \since This hint is available since SDL 3.2.0.
575
 */
576
#define SDL_HINT_BMP_SAVE_LEGACY_FORMAT "SDL_BMP_SAVE_LEGACY_FORMAT"
577
578
/**
579
 * A variable that decides what camera backend to use.
580
 *
581
 * By default, SDL will try all available camera backends in a reasonable
582
 * order until it finds one that can work, but this hint allows the app or
583
 * user to force a specific target, such as "directshow" if, say, you are on
584
 * Windows Media Foundations but want to try DirectShow instead.
585
 *
586
 * The default value is unset, in which case SDL will try to figure out the
587
 * best camera backend on your behalf. This hint needs to be set before
588
 * SDL_Init() is called to be useful.
589
 *
590
 * \since This hint is available since SDL 3.2.0.
591
 */
592
#define SDL_HINT_CAMERA_DRIVER "SDL_CAMERA_DRIVER"
593
594
/**
595
 * A variable that limits what CPU features are available.
596
 *
597
 * By default, SDL marks all features the current CPU supports as available.
598
 * This hint allows the enabled features to be limited to a subset.
599
 *
600
 * When the hint is unset, or empty, SDL will enable all detected CPU
601
 * features.
602
 *
603
 * The variable can be set to a comma separated list containing the following
604
 * items:
605
 *
606
 * - "all"
607
 * - "altivec"
608
 * - "sse"
609
 * - "sse2"
610
 * - "sse3"
611
 * - "sse41"
612
 * - "sse42"
613
 * - "avx"
614
 * - "avx2"
615
 * - "avx512f"
616
 * - "arm-simd"
617
 * - "neon"
618
 * - "lsx"
619
 * - "lasx"
620
 *
621
 * The items can be prefixed by '+'/'-' to add/remove features.
622
 *
623
 * \since This hint is available since SDL 3.2.0.
624
 */
625
#define SDL_HINT_CPU_FEATURE_MASK "SDL_CPU_FEATURE_MASK"
626
627
/**
628
 * A variable controlling whether DirectInput should be used for controllers.
629
 *
630
 * The variable can be set to the following values:
631
 *
632
 * - "0": Disable DirectInput detection.
633
 * - "1": Enable DirectInput detection. (default)
634
 *
635
 * This hint should be set before SDL is initialized.
636
 *
637
 * \since This hint is available since SDL 3.2.0.
638
 */
639
#define SDL_HINT_JOYSTICK_DIRECTINPUT "SDL_JOYSTICK_DIRECTINPUT"
640
641
/**
642
 * A variable that specifies a dialog backend to use.
643
 *
644
 * By default, SDL will try all available dialog backends in a reasonable
645
 * order until it finds one that can work, but this hint allows the app or
646
 * user to force a specific target.
647
 *
648
 * If the specified target does not exist or is not available, the
649
 * dialog-related function calls will fail.
650
 *
651
 * This hint currently only applies to platforms using the generic "Unix"
652
 * dialog implementation, but may be extended to more platforms in the future.
653
 * Note that some Unix and Unix-like platforms have their own implementation,
654
 * such as macOS and Haiku.
655
 *
656
 * The variable can be set to the following values:
657
 *
658
 * - NULL: Select automatically (default, all platforms)
659
 * - "portal": Use XDG Portals through DBus (Unix only)
660
 * - "zenity": Use the Zenity program (Unix only)
661
 *
662
 * More options may be added in the future.
663
 *
664
 * This hint can be set anytime.
665
 *
666
 * \since This hint is available since SDL 3.2.0.
667
 */
668
#define SDL_HINT_FILE_DIALOG_DRIVER "SDL_FILE_DIALOG_DRIVER"
669
670
/**
671
 * Override for SDL_GetDisplayUsableBounds().
672
 *
673
 * If set, this hint will override the expected results for
674
 * SDL_GetDisplayUsableBounds() for display index 0. Generally you don't want
675
 * to do this, but this allows an embedded system to request that some of the
676
 * screen be reserved for other uses when paired with a well-behaved
677
 * application.
678
 *
679
 * The contents of this hint must be 4 comma-separated integers, the first is
680
 * the bounds x, then y, width and height, in that order.
681
 *
682
 * This hint can be set anytime.
683
 *
684
 * \since This hint is available since SDL 3.2.0.
685
 */
686
#define SDL_HINT_DISPLAY_USABLE_BOUNDS "SDL_DISPLAY_USABLE_BOUNDS"
687
688
/**
689
 * Disable giving back control to the browser automatically when running with
690
 * asyncify.
691
 *
692
 * With -s ASYNCIFY, SDL calls emscripten_sleep during operations such as
693
 * refreshing the screen or polling events.
694
 *
695
 * This hint only applies to the emscripten platform.
696
 *
697
 * The variable can be set to the following values:
698
 *
699
 * - "0": Disable emscripten_sleep calls (if you give back browser control
700
 *   manually or use asyncify for other purposes).
701
 * - "1": Enable emscripten_sleep calls. (default)
702
 *
703
 * This hint can be set anytime.
704
 *
705
 * \since This hint is available since SDL 3.2.0.
706
 */
707
#define SDL_HINT_EMSCRIPTEN_ASYNCIFY "SDL_EMSCRIPTEN_ASYNCIFY"
708
709
/**
710
 * Specify the CSS selector used for the "default" window/canvas.
711
 *
712
 * This hint only applies to the emscripten platform.
713
 *
714
 * The default value is "#canvas"
715
 *
716
 * This hint should be set before creating a window.
717
 *
718
 * \since This hint is available since SDL 3.2.0.
719
 */
720
#define SDL_HINT_EMSCRIPTEN_CANVAS_SELECTOR "SDL_EMSCRIPTEN_CANVAS_SELECTOR"
721
722
/**
723
 * Override the binding element for keyboard inputs for Emscripten builds.
724
 *
725
 * This hint only applies to the emscripten platform.
726
 *
727
 * The variable can be one of:
728
 *
729
 * - "#window": the javascript window object (default)
730
 * - "#document": the javascript document object
731
 * - "#screen": the javascript window.screen object
732
 * - "#canvas": the WebGL canvas element
733
 * - "#none": Don't bind anything at all
734
 * - any other string without a leading # sign applies to the element on the
735
 *   page with that ID.
736
 *
737
 * This hint should be set before creating a window.
738
 *
739
 * \since This hint is available since SDL 3.2.0.
740
 */
741
#define SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT"
742
743
/**
744
 * A variable that controls whether the on-screen keyboard should be shown
745
 * when text input is active.
746
 *
747
 * The variable can be set to the following values:
748
 *
749
 * - "auto": The on-screen keyboard will be shown if there is no physical
750
 *   keyboard attached. (default)
751
 * - "0": Do not show the on-screen keyboard.
752
 * - "1": Show the on-screen keyboard, if available.
753
 *
754
 * This hint must be set before SDL_StartTextInput() is called
755
 *
756
 * \since This hint is available since SDL 3.2.0.
757
 */
758
#define SDL_HINT_ENABLE_SCREEN_KEYBOARD "SDL_ENABLE_SCREEN_KEYBOARD"
759
760
/**
761
 * A variable containing a list of evdev devices to use if udev is not
762
 * available.
763
 *
764
 * The list of devices is in the form:
765
 *
766
 * deviceclass:path[,deviceclass:path[,...]]
767
 *
768
 * where device class is an integer representing the SDL_UDEV_deviceclass and
769
 * path is the full path to the event device.
770
 *
771
 * This hint should be set before SDL is initialized.
772
 *
773
 * \since This hint is available since SDL 3.2.0.
774
 */
775
#define SDL_HINT_EVDEV_DEVICES "SDL_EVDEV_DEVICES"
776
777
/**
778
 * A variable controlling verbosity of the logging of SDL events pushed onto
779
 * the internal queue.
780
 *
781
 * The variable can be set to the following values, from least to most
782
 * verbose:
783
 *
784
 * - "0": Don't log any events. (default)
785
 * - "1": Log most events (other than the really spammy ones).
786
 * - "2": Include mouse and finger motion events.
787
 *
788
 * This is generally meant to be used to debug SDL itself, but can be useful
789
 * for application developers that need better visibility into what is going
790
 * on in the event queue. Logged events are sent through SDL_Log(), which
791
 * means by default they appear on stdout on most platforms or maybe
792
 * OutputDebugString() on Windows, and can be funneled by the app with
793
 * SDL_SetLogOutputFunction(), etc.
794
 *
795
 * This hint can be set anytime.
796
 *
797
 * \since This hint is available since SDL 3.2.0.
798
 */
799
#define SDL_HINT_EVENT_LOGGING "SDL_EVENT_LOGGING"
800
801
/**
802
 * A variable controlling whether raising the window should be done more
803
 * forcefully.
804
 *
805
 * The variable can be set to the following values:
806
 *
807
 * - "0": Honor the OS policy for raising windows. (default)
808
 * - "1": Force the window to be raised, overriding any OS policy.
809
 *
810
 * At present, this is only an issue under MS Windows, which makes it nearly
811
 * impossible to programmatically move a window to the foreground, for
812
 * "security" reasons. See http://stackoverflow.com/a/34414846 for a
813
 * discussion.
814
 *
815
 * This hint can be set anytime.
816
 *
817
 * \since This hint is available since SDL 3.2.0.
818
 */
819
#define SDL_HINT_FORCE_RAISEWINDOW "SDL_FORCE_RAISEWINDOW"
820
821
/**
822
 * A variable controlling how 3D acceleration is used to accelerate the SDL
823
 * screen surface.
824
 *
825
 * SDL can try to accelerate the SDL screen surface by using streaming
826
 * textures with a 3D rendering engine. This variable controls whether and how
827
 * this is done.
828
 *
829
 * The variable can be set to the following values:
830
 *
831
 * - "0": Disable 3D acceleration
832
 * - "1": Enable 3D acceleration, using the default renderer. (default)
833
 * - "X": Enable 3D acceleration, using X where X is one of the valid
834
 *   rendering drivers. (e.g. "direct3d", "opengl", etc.)
835
 *
836
 * This hint should be set before calling SDL_GetWindowSurface()
837
 *
838
 * \since This hint is available since SDL 3.2.0.
839
 */
840
#define SDL_HINT_FRAMEBUFFER_ACCELERATION "SDL_FRAMEBUFFER_ACCELERATION"
841
842
/**
843
 * A variable that lets you manually hint extra gamecontroller db entries.
844
 *
845
 * The variable should be newline delimited rows of gamecontroller config
846
 * data, see SDL_gamepad.h
847
 *
848
 * You can update mappings after SDL is initialized with
849
 * SDL_GetGamepadMappingForGUID() and SDL_AddGamepadMapping()
850
 *
851
 * This hint should be set before SDL is initialized.
852
 *
853
 * \since This hint is available since SDL 3.2.0.
854
 */
855
#define SDL_HINT_GAMECONTROLLERCONFIG "SDL_GAMECONTROLLERCONFIG"
856
857
/**
858
 * A variable that lets you provide a file with extra gamecontroller db
859
 * entries.
860
 *
861
 * The file should contain lines of gamecontroller config data, see
862
 * SDL_gamepad.h
863
 *
864
 * You can update mappings after SDL is initialized with
865
 * SDL_GetGamepadMappingForGUID() and SDL_AddGamepadMapping()
866
 *
867
 * This hint should be set before SDL is initialized.
868
 *
869
 * \since This hint is available since SDL 3.2.0.
870
 */
871
#define SDL_HINT_GAMECONTROLLERCONFIG_FILE "SDL_GAMECONTROLLERCONFIG_FILE"
872
873
/**
874
 * A variable that overrides the automatic controller type detection.
875
 *
876
 * The variable should be comma separated entries, in the form: VID/PID=type
877
 *
878
 * The VID and PID should be hexadecimal with exactly 4 digits, e.g. 0x00fd
879
 *
880
 * This hint affects what low level protocol is used with the HIDAPI driver.
881
 *
882
 * The variable can be set to the following values:
883
 *
884
 * - "Xbox360"
885
 * - "XboxOne"
886
 * - "PS3"
887
 * - "PS4"
888
 * - "PS5"
889
 * - "SwitchPro"
890
 *
891
 * This hint should be set before SDL is initialized.
892
 *
893
 * \since This hint is available since SDL 3.2.0.
894
 */
895
#define SDL_HINT_GAMECONTROLLERTYPE "SDL_GAMECONTROLLERTYPE"
896
897
/**
898
 * A variable containing a list of devices to skip when scanning for game
899
 * controllers.
900
 *
901
 * The format of the string is a comma separated list of USB VID/PID pairs in
902
 * hexadecimal form, e.g.
903
 *
904
 * 0xAAAA/0xBBBB,0xCCCC/0xDDDD
905
 *
906
 * The variable can also take the form of "@file", in which case the named
907
 * file will be loaded and interpreted as the value of the variable.
908
 *
909
 * This hint can be set anytime.
910
 *
911
 * \since This hint is available since SDL 3.2.0.
912
 */
913
#define SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES "SDL_GAMECONTROLLER_IGNORE_DEVICES"
914
915
/**
916
 * If set, all devices will be skipped when scanning for game controllers
917
 * except for the ones listed in this variable.
918
 *
919
 * The format of the string is a comma separated list of USB VID/PID pairs in
920
 * hexadecimal form, e.g.
921
 *
922
 * 0xAAAA/0xBBBB,0xCCCC/0xDDDD
923
 *
924
 * The variable can also take the form of "@file", in which case the named
925
 * file will be loaded and interpreted as the value of the variable.
926
 *
927
 * This hint can be set anytime.
928
 *
929
 * \since This hint is available since SDL 3.2.0.
930
 */
931
#define SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT"
932
933
/**
934
 * A variable that controls whether the device's built-in accelerometer and
935
 * gyro should be used as sensors for gamepads.
936
 *
937
 * The variable can be set to the following values:
938
 *
939
 * - "0": Sensor fusion is disabled
940
 * - "1": Sensor fusion is enabled for all controllers that lack sensors
941
 *
942
 * Or the variable can be a comma separated list of USB VID/PID pairs in
943
 * hexadecimal form, e.g.
944
 *
945
 * 0xAAAA/0xBBBB,0xCCCC/0xDDDD
946
 *
947
 * The variable can also take the form of "@file", in which case the named
948
 * file will be loaded and interpreted as the value of the variable.
949
 *
950
 * This hint should be set before a gamepad is opened.
951
 *
952
 * \since This hint is available since SDL 3.2.0.
953
 */
954
#define SDL_HINT_GAMECONTROLLER_SENSOR_FUSION "SDL_GAMECONTROLLER_SENSOR_FUSION"
955
956
/**
957
 * This variable sets the default text of the TextInput window on GDK
958
 * platforms.
959
 *
960
 * This hint is available only if SDL_GDK_TEXTINPUT defined.
961
 *
962
 * This hint should be set before calling SDL_StartTextInput()
963
 *
964
 * \since This hint is available since SDL 3.2.0.
965
 */
966
#define SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT "SDL_GDK_TEXTINPUT_DEFAULT_TEXT"
967
968
/**
969
 * This variable sets the description of the TextInput window on GDK
970
 * platforms.
971
 *
972
 * This hint is available only if SDL_GDK_TEXTINPUT defined.
973
 *
974
 * This hint should be set before calling SDL_StartTextInput()
975
 *
976
 * \since This hint is available since SDL 3.2.0.
977
 */
978
#define SDL_HINT_GDK_TEXTINPUT_DESCRIPTION "SDL_GDK_TEXTINPUT_DESCRIPTION"
979
980
/**
981
 * This variable sets the maximum input length of the TextInput window on GDK
982
 * platforms.
983
 *
984
 * The value must be a stringified integer, for example "10" to allow for up
985
 * to 10 characters of text input.
986
 *
987
 * This hint is available only if SDL_GDK_TEXTINPUT defined.
988
 *
989
 * This hint should be set before calling SDL_StartTextInput()
990
 *
991
 * \since This hint is available since SDL 3.2.0.
992
 */
993
#define SDL_HINT_GDK_TEXTINPUT_MAX_LENGTH "SDL_GDK_TEXTINPUT_MAX_LENGTH"
994
995
/**
996
 * This variable sets the input scope of the TextInput window on GDK
997
 * platforms.
998
 *
999
 * Set this hint to change the XGameUiTextEntryInputScope value that will be
1000
 * passed to the window creation function. The value must be a stringified
1001
 * integer, for example "0" for XGameUiTextEntryInputScope::Default.
1002
 *
1003
 * This hint is available only if SDL_GDK_TEXTINPUT defined.
1004
 *
1005
 * This hint should be set before calling SDL_StartTextInput()
1006
 *
1007
 * \since This hint is available since SDL 3.2.0.
1008
 */
1009
#define SDL_HINT_GDK_TEXTINPUT_SCOPE "SDL_GDK_TEXTINPUT_SCOPE"
1010
1011
/**
1012
 * This variable sets the title of the TextInput window on GDK platforms.
1013
 *
1014
 * This hint is available only if SDL_GDK_TEXTINPUT defined.
1015
 *
1016
 * This hint should be set before calling SDL_StartTextInput()
1017
 *
1018
 * \since This hint is available since SDL 3.2.0.
1019
 */
1020
#define SDL_HINT_GDK_TEXTINPUT_TITLE "SDL_GDK_TEXTINPUT_TITLE"
1021
1022
/**
1023
 * A variable to control whether HIDAPI uses libusb for device access.
1024
 *
1025
 * By default libusb will only be used for a few devices that require direct
1026
 * USB access, and this can be controlled with
1027
 * SDL_HINT_HIDAPI_LIBUSB_WHITELIST.
1028
 *
1029
 * The variable can be set to the following values:
1030
 *
1031
 * - "0": HIDAPI will not use libusb for device access.
1032
 * - "1": HIDAPI will use libusb for device access if available. (default)
1033
 *
1034
 * This hint should be set before SDL is initialized.
1035
 *
1036
 * \since This hint is available since SDL 3.2.0.
1037
 */
1038
#define SDL_HINT_HIDAPI_LIBUSB "SDL_HIDAPI_LIBUSB"
1039
1040
/**
1041
 * A variable to control whether HIDAPI uses libusb only for whitelisted
1042
 * devices.
1043
 *
1044
 * By default libusb will only be used for a few devices that require direct
1045
 * USB access.
1046
 *
1047
 * The variable can be set to the following values:
1048
 *
1049
 * - "0": HIDAPI will use libusb for all device access.
1050
 * - "1": HIDAPI will use libusb only for whitelisted devices. (default)
1051
 *
1052
 * This hint should be set before SDL is initialized.
1053
 *
1054
 * \since This hint is available since SDL 3.2.0.
1055
 */
1056
#define SDL_HINT_HIDAPI_LIBUSB_WHITELIST "SDL_HIDAPI_LIBUSB_WHITELIST"
1057
1058
/**
1059
 * A variable to control whether HIDAPI uses udev for device detection.
1060
 *
1061
 * The variable can be set to the following values:
1062
 *
1063
 * - "0": HIDAPI will poll for device changes.
1064
 * - "1": HIDAPI will use udev for device detection. (default)
1065
 *
1066
 * This hint should be set before SDL is initialized.
1067
 *
1068
 * \since This hint is available since SDL 3.2.0.
1069
 */
1070
#define SDL_HINT_HIDAPI_UDEV "SDL_HIDAPI_UDEV"
1071
1072
/**
1073
 * A variable that specifies a GPU backend to use.
1074
 *
1075
 * By default, SDL will try all available GPU backends in a reasonable order
1076
 * until it finds one that can work, but this hint allows the app or user to
1077
 * force a specific target, such as "direct3d12" if, say, your hardware
1078
 * supports Vulkan but you want to try using D3D12 instead.
1079
 *
1080
 * This hint should be set before any GPU functions are called.
1081
 *
1082
 * \since This hint is available since SDL 3.2.0.
1083
 */
1084
#define SDL_HINT_GPU_DRIVER "SDL_GPU_DRIVER"
1085
1086
/**
1087
 * A variable to control whether SDL_hid_enumerate() enumerates all HID
1088
 * devices or only controllers.
1089
 *
1090
 * The variable can be set to the following values:
1091
 *
1092
 * - "0": SDL_hid_enumerate() will enumerate all HID devices.
1093
 * - "1": SDL_hid_enumerate() will only enumerate controllers. (default)
1094
 *
1095
 * By default SDL will only enumerate controllers, to reduce risk of hanging
1096
 * or crashing on devices with bad drivers and avoiding macOS keyboard capture
1097
 * permission prompts.
1098
 *
1099
 * This hint can be set anytime.
1100
 *
1101
 * \since This hint is available since SDL 3.2.0.
1102
 */
1103
#define SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS "SDL_HIDAPI_ENUMERATE_ONLY_CONTROLLERS"
1104
1105
/**
1106
 * A variable containing a list of devices to ignore in SDL_hid_enumerate().
1107
 *
1108
 * The format of the string is a comma separated list of USB VID/PID pairs in
1109
 * hexadecimal form, e.g.
1110
 *
1111
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1112
 *
1113
 * For example, to ignore the Shanwan DS3 controller and any Valve controller,
1114
 * you might use the string "0x2563/0x0523,0x28de/0x0000"
1115
 *
1116
 * This hint can be set anytime.
1117
 *
1118
 * \since This hint is available since SDL 3.2.0.
1119
 */
1120
#define SDL_HINT_HIDAPI_IGNORE_DEVICES "SDL_HIDAPI_IGNORE_DEVICES"
1121
1122
/**
1123
 * A variable describing what IME UI elements the application can display.
1124
 *
1125
 * By default IME UI is handled using native components by the OS where
1126
 * possible, however this can interfere with or not be visible when exclusive
1127
 * fullscreen mode is used.
1128
 *
1129
 * The variable can be set to a comma separated list containing the following
1130
 * items:
1131
 *
1132
 * - "none" or "0": The application can't render any IME elements, and native
1133
 *   UI should be used. (default)
1134
 * - "composition": The application handles SDL_EVENT_TEXT_EDITING events and
1135
 *   can render the composition text.
1136
 * - "candidates": The application handles SDL_EVENT_TEXT_EDITING_CANDIDATES
1137
 *   and can render the candidate list.
1138
 *
1139
 * This hint should be set before SDL is initialized.
1140
 *
1141
 * \since This hint is available since SDL 3.2.0.
1142
 */
1143
#define SDL_HINT_IME_IMPLEMENTED_UI "SDL_IME_IMPLEMENTED_UI"
1144
1145
/**
1146
 * A variable controlling whether the home indicator bar on iPhone X should be
1147
 * hidden.
1148
 *
1149
 * The variable can be set to the following values:
1150
 *
1151
 * - "0": The indicator bar is not hidden. (default for windowed applications)
1152
 * - "1": The indicator bar is hidden and is shown when the screen is touched
1153
 *   (useful for movie playback applications).
1154
 * - "2": The indicator bar is dim and the first swipe makes it visible and
1155
 *   the second swipe performs the "home" action. (default for fullscreen
1156
 *   applications)
1157
 *
1158
 * This hint can be set anytime.
1159
 *
1160
 * \since This hint is available since SDL 3.2.0.
1161
 */
1162
#define SDL_HINT_IOS_HIDE_HOME_INDICATOR "SDL_IOS_HIDE_HOME_INDICATOR"
1163
1164
/**
1165
 * A variable that lets you enable joystick (and gamecontroller) events even
1166
 * when your app is in the background.
1167
 *
1168
 * The variable can be set to the following values:
1169
 *
1170
 * - "0": Disable joystick & gamecontroller input events when the application
1171
 *   is in the background. (default)
1172
 * - "1": Enable joystick & gamecontroller input events when the application
1173
 *   is in the background.
1174
 *
1175
 * This hint can be set anytime.
1176
 *
1177
 * \since This hint is available since SDL 3.2.0.
1178
 */
1179
#define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"
1180
1181
/**
1182
 * A variable containing a list of arcade stick style controllers.
1183
 *
1184
 * The format of the string is a comma separated list of USB VID/PID pairs in
1185
 * hexadecimal form, e.g.
1186
 *
1187
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1188
 *
1189
 * The variable can also take the form of "@file", in which case the named
1190
 * file will be loaded and interpreted as the value of the variable.
1191
 *
1192
 * This hint can be set anytime.
1193
 *
1194
 * \since This hint is available since SDL 3.2.0.
1195
 */
1196
#define SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES "SDL_JOYSTICK_ARCADESTICK_DEVICES"
1197
1198
/**
1199
 * A variable containing a list of devices that are not arcade stick style
1200
 * controllers.
1201
 *
1202
 * This will override SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES and the built in
1203
 * device list.
1204
 *
1205
 * The format of the string is a comma separated list of USB VID/PID pairs in
1206
 * hexadecimal form, e.g.
1207
 *
1208
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1209
 *
1210
 * The variable can also take the form of "@file", in which case the named
1211
 * file will be loaded and interpreted as the value of the variable.
1212
 *
1213
 * This hint can be set anytime.
1214
 *
1215
 * \since This hint is available since SDL 3.2.0.
1216
 */
1217
#define SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED "SDL_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED"
1218
1219
/**
1220
 * A variable containing a list of devices that should not be considered
1221
 * joysticks.
1222
 *
1223
 * The format of the string is a comma separated list of USB VID/PID pairs in
1224
 * hexadecimal form, e.g.
1225
 *
1226
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1227
 *
1228
 * The variable can also take the form of "@file", in which case the named
1229
 * file will be loaded and interpreted as the value of the variable.
1230
 *
1231
 * This hint can be set anytime.
1232
 *
1233
 * \since This hint is available since SDL 3.2.0.
1234
 */
1235
#define SDL_HINT_JOYSTICK_BLACKLIST_DEVICES "SDL_JOYSTICK_BLACKLIST_DEVICES"
1236
1237
/**
1238
 * A variable containing a list of devices that should be considered
1239
 * joysticks.
1240
 *
1241
 * This will override SDL_HINT_JOYSTICK_BLACKLIST_DEVICES and the built in
1242
 * device list.
1243
 *
1244
 * The format of the string is a comma separated list of USB VID/PID pairs in
1245
 * hexadecimal form, e.g.
1246
 *
1247
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1248
 *
1249
 * The variable can also take the form of "@file", in which case the named
1250
 * file will be loaded and interpreted as the value of the variable.
1251
 *
1252
 * This hint can be set anytime.
1253
 *
1254
 * \since This hint is available since SDL 3.2.0.
1255
 */
1256
#define SDL_HINT_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED "SDL_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED"
1257
1258
/**
1259
 * A variable containing a comma separated list of devices to open as
1260
 * joysticks.
1261
 *
1262
 * This variable is currently only used by the Linux joystick driver.
1263
 *
1264
 * \since This hint is available since SDL 3.2.0.
1265
 */
1266
#define SDL_HINT_JOYSTICK_DEVICE "SDL_JOYSTICK_DEVICE"
1267
1268
/**
1269
 * A variable controlling whether enhanced reports should be used for
1270
 * controllers when using the HIDAPI driver.
1271
 *
1272
 * Enhanced reports allow rumble and effects on Bluetooth PlayStation
1273
 * controllers and gyro on Nintendo Switch controllers, but break Windows
1274
 * DirectInput for other applications that don't use SDL.
1275
 *
1276
 * Once enhanced reports are enabled, they can't be disabled on PlayStation
1277
 * controllers without power cycling the controller.
1278
 *
1279
 * The variable can be set to the following values:
1280
 *
1281
 * - "0": enhanced reports are not enabled.
1282
 * - "1": enhanced reports are enabled. (default)
1283
 * - "auto": enhanced features are advertised to the application, but SDL
1284
 *   doesn't change the controller report mode unless the application uses
1285
 *   them.
1286
 *
1287
 * This hint can be enabled anytime.
1288
 *
1289
 * \since This hint is available since SDL 3.2.0.
1290
 */
1291
#define SDL_HINT_JOYSTICK_ENHANCED_REPORTS "SDL_JOYSTICK_ENHANCED_REPORTS"
1292
1293
/**
1294
 * A variable containing a list of flightstick style controllers.
1295
 *
1296
 * The format of the string is a comma separated list of USB VID/PID pairs in
1297
 * hexadecimal form, e.g.
1298
 *
1299
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1300
 *
1301
 * The variable can also take the form of @file, in which case the named file
1302
 * will be loaded and interpreted as the value of the variable.
1303
 *
1304
 * This hint can be set anytime.
1305
 *
1306
 * \since This hint is available since SDL 3.2.0.
1307
 */
1308
#define SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES "SDL_JOYSTICK_FLIGHTSTICK_DEVICES"
1309
1310
/**
1311
 * A variable containing a list of devices that are not flightstick style
1312
 * controllers.
1313
 *
1314
 * This will override SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES and the built in
1315
 * device list.
1316
 *
1317
 * The format of the string is a comma separated list of USB VID/PID pairs in
1318
 * hexadecimal form, e.g.
1319
 *
1320
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1321
 *
1322
 * The variable can also take the form of "@file", in which case the named
1323
 * file will be loaded and interpreted as the value of the variable.
1324
 *
1325
 * This hint can be set anytime.
1326
 *
1327
 * \since This hint is available since SDL 3.2.0.
1328
 */
1329
#define SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED "SDL_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED"
1330
1331
/**
1332
 * A variable controlling whether GameInput should be used for controller
1333
 * handling on Windows.
1334
 *
1335
 * The variable can be set to the following values:
1336
 *
1337
 * - "0": GameInput is not used.
1338
 * - "1": GameInput is used.
1339
 *
1340
 * The default is "1" on GDK platforms, and "0" otherwise.
1341
 *
1342
 * This hint should be set before SDL is initialized.
1343
 *
1344
 * \since This hint is available since SDL 3.2.0.
1345
 */
1346
#define SDL_HINT_JOYSTICK_GAMEINPUT "SDL_JOYSTICK_GAMEINPUT"
1347
1348
/**
1349
 * A variable containing a list of devices known to have a GameCube form
1350
 * factor.
1351
 *
1352
 * The format of the string is a comma separated list of USB VID/PID pairs in
1353
 * hexadecimal form, e.g.
1354
 *
1355
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1356
 *
1357
 * The variable can also take the form of "@file", in which case the named
1358
 * file will be loaded and interpreted as the value of the variable.
1359
 *
1360
 * This hint can be set anytime.
1361
 *
1362
 * \since This hint is available since SDL 3.2.0.
1363
 */
1364
#define SDL_HINT_JOYSTICK_GAMECUBE_DEVICES "SDL_JOYSTICK_GAMECUBE_DEVICES"
1365
1366
/**
1367
 * A variable containing a list of devices known not to have a GameCube form
1368
 * factor.
1369
 *
1370
 * This will override SDL_HINT_JOYSTICK_GAMECUBE_DEVICES and the built in
1371
 * device list.
1372
 *
1373
 * The format of the string is a comma separated list of USB VID/PID pairs in
1374
 * hexadecimal form, e.g.
1375
 *
1376
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
1377
 *
1378
 * The variable can also take the form of "@file", in which case the named
1379
 * file will be loaded and interpreted as the value of the variable.
1380
 *
1381
 * This hint can be set anytime.
1382
 *
1383
 * \since This hint is available since SDL 3.2.0.
1384
 */
1385
#define SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED "SDL_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED"
1386
1387
/**
1388
 * A variable controlling whether the HIDAPI joystick drivers should be used.
1389
 *
1390
 * The variable can be set to the following values:
1391
 *
1392
 * - "0": HIDAPI drivers are not used.
1393
 * - "1": HIDAPI drivers are used. (default)
1394
 *
1395
 * This variable is the default for all drivers, but can be overridden by the
1396
 * hints for specific drivers below.
1397
 *
1398
 * This hint should be set before initializing joysticks and gamepads.
1399
 *
1400
 * \since This hint is available since SDL 3.2.0.
1401
 */
1402
#define SDL_HINT_JOYSTICK_HIDAPI "SDL_JOYSTICK_HIDAPI"
1403
1404
/**
1405
 * A variable controlling whether Nintendo Switch Joy-Con controllers will be
1406
 * combined into a single Pro-like controller when using the HIDAPI driver.
1407
 *
1408
 * The variable can be set to the following values:
1409
 *
1410
 * - "0": Left and right Joy-Con controllers will not be combined and each
1411
 *   will be a mini-gamepad.
1412
 * - "1": Left and right Joy-Con controllers will be combined into a single
1413
 *   controller. (default)
1414
 *
1415
 * This hint should be set before initializing joysticks and gamepads.
1416
 *
1417
 * \since This hint is available since SDL 3.2.0.
1418
 */
1419
#define SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS"
1420
1421
/**
1422
 * A variable controlling whether the HIDAPI driver for Nintendo GameCube
1423
 * controllers should be used.
1424
 *
1425
 * The variable can be set to the following values:
1426
 *
1427
 * - "0": HIDAPI driver is not used.
1428
 * - "1": HIDAPI driver is used.
1429
 *
1430
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
1431
 *
1432
 * This hint should be set before initializing joysticks and gamepads.
1433
 *
1434
 * \since This hint is available since SDL 3.2.0.
1435
 */
1436
#define SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE "SDL_JOYSTICK_HIDAPI_GAMECUBE"
1437
1438
/**
1439
 * A variable controlling whether rumble is used to implement the GameCube
1440
 * controller's 3 rumble modes, Stop(0), Rumble(1), and StopHard(2).
1441
 *
1442
 * This is useful for applications that need full compatibility for things
1443
 * like ADSR envelopes. - Stop is implemented by setting low_frequency_rumble
1444
 * to 0 and high_frequency_rumble >0 - Rumble is both at any arbitrary value -
1445
 * StopHard is implemented by setting both low_frequency_rumble and
1446
 * high_frequency_rumble to 0
1447
 *
1448
 * The variable can be set to the following values:
1449
 *
1450
 * - "0": Normal rumble behavior is behavior is used. (default)
1451
 * - "1": Proper GameCube controller rumble behavior is used.
1452
 *
1453
 * This hint can be set anytime.
1454
 *
1455
 * \since This hint is available since SDL 3.2.0.
1456
 */
1457
#define SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE "SDL_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE"
1458
1459
/**
1460
 * A variable controlling whether the HIDAPI driver for Nintendo Switch
1461
 * Joy-Cons should be used.
1462
 *
1463
 * The variable can be set to the following values:
1464
 *
1465
 * - "0": HIDAPI driver is not used.
1466
 * - "1": HIDAPI driver is used.
1467
 *
1468
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1469
 *
1470
 * This hint should be set before initializing joysticks and gamepads.
1471
 *
1472
 * \since This hint is available since SDL 3.2.0.
1473
 */
1474
#define SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS "SDL_JOYSTICK_HIDAPI_JOY_CONS"
1475
1476
/**
1477
 * A variable controlling whether the Home button LED should be turned on when
1478
 * a Nintendo Switch Joy-Con controller is opened.
1479
 *
1480
 * The variable can be set to the following values:
1481
 *
1482
 * - "0": home button LED is turned off
1483
 * - "1": home button LED is turned on
1484
 *
1485
 * By default the Home button LED state is not changed. This hint can also be
1486
 * set to a floating point value between 0.0 and 1.0 which controls the
1487
 * brightness of the Home button LED.
1488
 *
1489
 * This hint can be set anytime.
1490
 *
1491
 * \since This hint is available since SDL 3.2.0.
1492
 */
1493
#define SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED "SDL_JOYSTICK_HIDAPI_JOYCON_HOME_LED"
1494
1495
/**
1496
 * A variable controlling whether the HIDAPI driver for Amazon Luna
1497
 * controllers connected via Bluetooth should be used.
1498
 *
1499
 * The variable can be set to the following values:
1500
 *
1501
 * - "0": HIDAPI driver is not used.
1502
 * - "1": HIDAPI driver is used.
1503
 *
1504
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1505
 *
1506
 * This hint should be set before initializing joysticks and gamepads.
1507
 *
1508
 * \since This hint is available since SDL 3.2.0.
1509
 */
1510
#define SDL_HINT_JOYSTICK_HIDAPI_LUNA "SDL_JOYSTICK_HIDAPI_LUNA"
1511
1512
/**
1513
 * A variable controlling whether the HIDAPI driver for Nintendo Online
1514
 * classic controllers should be used.
1515
 *
1516
 * The variable can be set to the following values:
1517
 *
1518
 * - "0": HIDAPI driver is not used.
1519
 * - "1": HIDAPI driver is used.
1520
 *
1521
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1522
 *
1523
 * This hint should be set before initializing joysticks and gamepads.
1524
 *
1525
 * \since This hint is available since SDL 3.2.0.
1526
 */
1527
#define SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC "SDL_JOYSTICK_HIDAPI_NINTENDO_CLASSIC"
1528
1529
/**
1530
 * A variable controlling whether the HIDAPI driver for PS3 controllers should
1531
 * be used.
1532
 *
1533
 * The variable can be set to the following values:
1534
 *
1535
 * - "0": HIDAPI driver is not used.
1536
 * - "1": HIDAPI driver is used.
1537
 *
1538
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI on macOS, and "0" on
1539
 * other platforms.
1540
 *
1541
 * For official Sony driver (sixaxis.sys) use
1542
 * SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER. See
1543
 * https://github.com/ViGEm/DsHidMini for an alternative driver on Windows.
1544
 *
1545
 * This hint should be set before initializing joysticks and gamepads.
1546
 *
1547
 * \since This hint is available since SDL 3.2.0.
1548
 */
1549
#define SDL_HINT_JOYSTICK_HIDAPI_PS3 "SDL_JOYSTICK_HIDAPI_PS3"
1550
1551
/**
1552
 * A variable controlling whether the Sony driver (sixaxis.sys) for PS3
1553
 * controllers (Sixaxis/DualShock 3) should be used.
1554
 *
1555
 * The variable can be set to the following values:
1556
 *
1557
 * - "0": Sony driver (sixaxis.sys) is not used.
1558
 * - "1": Sony driver (sixaxis.sys) is used.
1559
 *
1560
 * The default value is 0.
1561
 *
1562
 * This hint should be set before initializing joysticks and gamepads.
1563
 *
1564
 * \since This hint is available since SDL 3.2.0.
1565
 */
1566
#define SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER "SDL_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER"
1567
1568
/**
1569
 * A variable controlling whether the HIDAPI driver for PS4 controllers should
1570
 * be used.
1571
 *
1572
 * The variable can be set to the following values:
1573
 *
1574
 * - "0": HIDAPI driver is not used.
1575
 * - "1": HIDAPI driver is used.
1576
 *
1577
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1578
 *
1579
 * This hint should be set before initializing joysticks and gamepads.
1580
 *
1581
 * \since This hint is available since SDL 3.2.0.
1582
 */
1583
#define SDL_HINT_JOYSTICK_HIDAPI_PS4 "SDL_JOYSTICK_HIDAPI_PS4"
1584
1585
/**
1586
 * A variable controlling the update rate of the PS4 controller over Bluetooth
1587
 * when using the HIDAPI driver.
1588
 *
1589
 * This defaults to 4 ms, to match the behavior over USB, and to be more
1590
 * friendly to other Bluetooth devices and older Bluetooth hardware on the
1591
 * computer. It can be set to "1" (1000Hz), "2" (500Hz) and "4" (250Hz)
1592
 *
1593
 * This hint can be set anytime, but only takes effect when extended input
1594
 * reports are enabled.
1595
 *
1596
 * \since This hint is available since SDL 3.2.0.
1597
 */
1598
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL "SDL_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL"
1599
1600
/**
1601
 * A variable controlling whether the HIDAPI driver for PS5 controllers should
1602
 * be used.
1603
 *
1604
 * The variable can be set to the following values:
1605
 *
1606
 * - "0": HIDAPI driver is not used.
1607
 * - "1": HIDAPI driver is used.
1608
 *
1609
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1610
 *
1611
 * This hint should be set before initializing joysticks and gamepads.
1612
 *
1613
 * \since This hint is available since SDL 3.2.0.
1614
 */
1615
#define SDL_HINT_JOYSTICK_HIDAPI_PS5 "SDL_JOYSTICK_HIDAPI_PS5"
1616
1617
/**
1618
 * A variable controlling whether the player LEDs should be lit to indicate
1619
 * which player is associated with a PS5 controller.
1620
 *
1621
 * The variable can be set to the following values:
1622
 *
1623
 * - "0": player LEDs are not enabled.
1624
 * - "1": player LEDs are enabled. (default)
1625
 *
1626
 * \since This hint is available since SDL 3.2.0.
1627
 */
1628
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"
1629
1630
/**
1631
 * A variable controlling whether the HIDAPI driver for NVIDIA SHIELD
1632
 * controllers should be used.
1633
 *
1634
 * The variable can be set to the following values:
1635
 *
1636
 * - "0": HIDAPI driver is not used.
1637
 * - "1": HIDAPI driver is used.
1638
 *
1639
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1640
 *
1641
 * This hint should be set before initializing joysticks and gamepads.
1642
 *
1643
 * \since This hint is available since SDL 3.2.0.
1644
 */
1645
#define SDL_HINT_JOYSTICK_HIDAPI_SHIELD "SDL_JOYSTICK_HIDAPI_SHIELD"
1646
1647
/**
1648
 * A variable controlling whether the HIDAPI driver for Google Stadia
1649
 * controllers should be used.
1650
 *
1651
 * The variable can be set to the following values:
1652
 *
1653
 * - "0": HIDAPI driver is not used.
1654
 * - "1": HIDAPI driver is used.
1655
 *
1656
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1657
 *
1658
 * \since This hint is available since SDL 3.2.0.
1659
 */
1660
#define SDL_HINT_JOYSTICK_HIDAPI_STADIA "SDL_JOYSTICK_HIDAPI_STADIA"
1661
1662
/**
1663
 * A variable controlling whether the HIDAPI driver for Bluetooth Steam
1664
 * Controllers should be used.
1665
 *
1666
 * The variable can be set to the following values:
1667
 *
1668
 * - "0": HIDAPI driver is not used. (default)
1669
 * - "1": HIDAPI driver is used for Steam Controllers, which requires
1670
 *   Bluetooth access and may prompt the user for permission on iOS and
1671
 *   Android.
1672
 *
1673
 * This hint should be set before initializing joysticks and gamepads.
1674
 *
1675
 * \since This hint is available since SDL 3.2.0.
1676
 */
1677
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM "SDL_JOYSTICK_HIDAPI_STEAM"
1678
1679
/**
1680
 * A variable controlling whether the Steam button LED should be turned on
1681
 * when a Steam controller is opened.
1682
 *
1683
 * The variable can be set to the following values:
1684
 *
1685
 * - "0": Steam button LED is turned off.
1686
 * - "1": Steam button LED is turned on.
1687
 *
1688
 * By default the Steam button LED state is not changed. This hint can also be
1689
 * set to a floating point value between 0.0 and 1.0 which controls the
1690
 * brightness of the Steam button LED.
1691
 *
1692
 * This hint can be set anytime.
1693
 *
1694
 * \since This hint is available since SDL 3.2.0.
1695
 */
1696
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED "SDL_JOYSTICK_HIDAPI_STEAM_HOME_LED"
1697
1698
/**
1699
 * A variable controlling whether the HIDAPI driver for the Steam Deck builtin
1700
 * controller should be used.
1701
 *
1702
 * The variable can be set to the following values:
1703
 *
1704
 * - "0": HIDAPI driver is not used.
1705
 * - "1": HIDAPI driver is used.
1706
 *
1707
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1708
 *
1709
 * This hint should be set before initializing joysticks and gamepads.
1710
 *
1711
 * \since This hint is available since SDL 3.2.0.
1712
 */
1713
#define SDL_HINT_JOYSTICK_HIDAPI_STEAMDECK "SDL_JOYSTICK_HIDAPI_STEAMDECK"
1714
1715
/**
1716
 * A variable controlling whether the HIDAPI driver for HORI licensed Steam
1717
 * controllers should be used.
1718
 *
1719
 * This variable can be set to the following values: "0" - HIDAPI driver is
1720
 * not used "1" - HIDAPI driver is used
1721
 *
1722
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI
1723
 */
1724
#define SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI "SDL_JOYSTICK_HIDAPI_STEAM_HORI"
1725
1726
/**
1727
 * A variable controlling whether the HIDAPI driver for Nintendo Switch
1728
 * controllers should be used.
1729
 *
1730
 * The variable can be set to the following values:
1731
 *
1732
 * - "0": HIDAPI driver is not used.
1733
 * - "1": HIDAPI driver is used.
1734
 *
1735
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI.
1736
 *
1737
 * This hint should be set before initializing joysticks and gamepads.
1738
 *
1739
 * \since This hint is available since SDL 3.2.0.
1740
 */
1741
#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH "SDL_JOYSTICK_HIDAPI_SWITCH"
1742
1743
/**
1744
 * A variable controlling whether the Home button LED should be turned on when
1745
 * a Nintendo Switch Pro controller is opened.
1746
 *
1747
 * The variable can be set to the following values:
1748
 *
1749
 * - "0": Home button LED is turned off.
1750
 * - "1": Home button LED is turned on.
1751
 *
1752
 * By default the Home button LED state is not changed. This hint can also be
1753
 * set to a floating point value between 0.0 and 1.0 which controls the
1754
 * brightness of the Home button LED.
1755
 *
1756
 * This hint can be set anytime.
1757
 *
1758
 * \since This hint is available since SDL 3.2.0.
1759
 */
1760
#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED "SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED"
1761
1762
/**
1763
 * A variable controlling whether the player LEDs should be lit to indicate
1764
 * which player is associated with a Nintendo Switch controller.
1765
 *
1766
 * The variable can be set to the following values:
1767
 *
1768
 * - "0": Player LEDs are not enabled.
1769
 * - "1": Player LEDs are enabled. (default)
1770
 *
1771
 * This hint can be set anytime.
1772
 *
1773
 * \since This hint is available since SDL 3.2.0.
1774
 */
1775
#define SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED "SDL_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED"
1776
1777
/**
1778
 * A variable controlling whether Nintendo Switch Joy-Con controllers will be
1779
 * in vertical mode when using the HIDAPI driver.
1780
 *
1781
 * The variable can be set to the following values:
1782
 *
1783
 * - "0": Left and right Joy-Con controllers will not be in vertical mode.
1784
 *   (default)
1785
 * - "1": Left and right Joy-Con controllers will be in vertical mode.
1786
 *
1787
 * This hint should be set before opening a Joy-Con controller.
1788
 *
1789
 * \since This hint is available since SDL 3.2.0.
1790
 */
1791
#define SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS "SDL_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS"
1792
1793
/**
1794
 * A variable controlling whether the HIDAPI driver for Nintendo Wii and Wii U
1795
 * controllers should be used.
1796
 *
1797
 * The variable can be set to the following values:
1798
 *
1799
 * - "0": HIDAPI driver is not used.
1800
 * - "1": HIDAPI driver is used.
1801
 *
1802
 * This driver doesn't work with the dolphinbar, so the default is false for
1803
 * now.
1804
 *
1805
 * This hint should be set before initializing joysticks and gamepads.
1806
 *
1807
 * \since This hint is available since SDL 3.2.0.
1808
 */
1809
#define SDL_HINT_JOYSTICK_HIDAPI_WII "SDL_JOYSTICK_HIDAPI_WII"
1810
1811
/**
1812
 * A variable controlling whether the player LEDs should be lit to indicate
1813
 * which player is associated with a Wii controller.
1814
 *
1815
 * The variable can be set to the following values:
1816
 *
1817
 * - "0": Player LEDs are not enabled.
1818
 * - "1": Player LEDs are enabled. (default)
1819
 *
1820
 * This hint can be set anytime.
1821
 *
1822
 * \since This hint is available since SDL 3.2.0.
1823
 */
1824
#define SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED "SDL_JOYSTICK_HIDAPI_WII_PLAYER_LED"
1825
1826
/**
1827
 * A variable controlling whether the HIDAPI driver for XBox controllers
1828
 * should be used.
1829
 *
1830
 * The variable can be set to the following values:
1831
 *
1832
 * - "0": HIDAPI driver is not used.
1833
 * - "1": HIDAPI driver is used.
1834
 *
1835
 * The default is "0" on Windows, otherwise the value of
1836
 * SDL_HINT_JOYSTICK_HIDAPI
1837
 *
1838
 * This hint should be set before initializing joysticks and gamepads.
1839
 *
1840
 * \since This hint is available since SDL 3.2.0.
1841
 */
1842
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX "SDL_JOYSTICK_HIDAPI_XBOX"
1843
1844
/**
1845
 * A variable controlling whether the HIDAPI driver for XBox 360 controllers
1846
 * should be used.
1847
 *
1848
 * The variable can be set to the following values:
1849
 *
1850
 * - "0": HIDAPI driver is not used.
1851
 * - "1": HIDAPI driver is used.
1852
 *
1853
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI_XBOX
1854
 *
1855
 * This hint should be set before initializing joysticks and gamepads.
1856
 *
1857
 * \since This hint is available since SDL 3.2.0.
1858
 */
1859
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX_360 "SDL_JOYSTICK_HIDAPI_XBOX_360"
1860
1861
/**
1862
 * A variable controlling whether the player LEDs should be lit to indicate
1863
 * which player is associated with an Xbox 360 controller.
1864
 *
1865
 * The variable can be set to the following values:
1866
 *
1867
 * - "0": Player LEDs are not enabled.
1868
 * - "1": Player LEDs are enabled. (default)
1869
 *
1870
 * This hint can be set anytime.
1871
 *
1872
 * \since This hint is available since SDL 3.2.0.
1873
 */
1874
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED "SDL_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED"
1875
1876
/**
1877
 * A variable controlling whether the HIDAPI driver for XBox 360 wireless
1878
 * controllers should be used.
1879
 *
1880
 * The variable can be set to the following values:
1881
 *
1882
 * - "0": HIDAPI driver is not used.
1883
 * - "1": HIDAPI driver is used.
1884
 *
1885
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI_XBOX_360
1886
 *
1887
 * This hint should be set before initializing joysticks and gamepads.
1888
 *
1889
 * \since This hint is available since SDL 3.2.0.
1890
 */
1891
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS "SDL_JOYSTICK_HIDAPI_XBOX_360_WIRELESS"
1892
1893
/**
1894
 * A variable controlling whether the HIDAPI driver for XBox One controllers
1895
 * should be used.
1896
 *
1897
 * The variable can be set to the following values:
1898
 *
1899
 * - "0": HIDAPI driver is not used.
1900
 * - "1": HIDAPI driver is used.
1901
 *
1902
 * The default is the value of SDL_HINT_JOYSTICK_HIDAPI_XBOX.
1903
 *
1904
 * This hint should be set before initializing joysticks and gamepads.
1905
 *
1906
 * \since This hint is available since SDL 3.2.0.
1907
 */
1908
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE "SDL_JOYSTICK_HIDAPI_XBOX_ONE"
1909
1910
/**
1911
 * A variable controlling whether the Home button LED should be turned on when
1912
 * an Xbox One controller is opened.
1913
 *
1914
 * The variable can be set to the following values:
1915
 *
1916
 * - "0": Home button LED is turned off.
1917
 * - "1": Home button LED is turned on.
1918
 *
1919
 * By default the Home button LED state is not changed. This hint can also be
1920
 * set to a floating point value between 0.0 and 1.0 which controls the
1921
 * brightness of the Home button LED. The default brightness is 0.4.
1922
 *
1923
 * This hint can be set anytime.
1924
 *
1925
 * \since This hint is available since SDL 3.2.0.
1926
 */
1927
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED "SDL_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED"
1928
1929
/**
1930
 * A variable controlling whether IOKit should be used for controller
1931
 * handling.
1932
 *
1933
 * The variable can be set to the following values:
1934
 *
1935
 * - "0": IOKit is not used.
1936
 * - "1": IOKit is used. (default)
1937
 *
1938
 * This hint should be set before SDL is initialized.
1939
 *
1940
 * \since This hint is available since SDL 3.2.0.
1941
 */
1942
#define SDL_HINT_JOYSTICK_IOKIT "SDL_JOYSTICK_IOKIT"
1943
1944
/**
1945
 * A variable controlling whether to use the classic /dev/input/js* joystick
1946
 * interface or the newer /dev/input/event* joystick interface on Linux.
1947
 *
1948
 * The variable can be set to the following values:
1949
 *
1950
 * - "0": Use /dev/input/event* (default)
1951
 * - "1": Use /dev/input/js*
1952
 *
1953
 * This hint should be set before SDL is initialized.
1954
 *
1955
 * \since This hint is available since SDL 3.2.0.
1956
 */
1957
#define SDL_HINT_JOYSTICK_LINUX_CLASSIC "SDL_JOYSTICK_LINUX_CLASSIC"
1958
1959
/**
1960
 * A variable controlling whether joysticks on Linux adhere to their
1961
 * HID-defined deadzones or return unfiltered values.
1962
 *
1963
 * The variable can be set to the following values:
1964
 *
1965
 * - "0": Return unfiltered joystick axis values. (default)
1966
 * - "1": Return axis values with deadzones taken into account.
1967
 *
1968
 * This hint should be set before a controller is opened.
1969
 *
1970
 * \since This hint is available since SDL 3.2.0.
1971
 */
1972
#define SDL_HINT_JOYSTICK_LINUX_DEADZONES "SDL_JOYSTICK_LINUX_DEADZONES"
1973
1974
/**
1975
 * A variable controlling whether joysticks on Linux will always treat 'hat'
1976
 * axis inputs (ABS_HAT0X - ABS_HAT3Y) as 8-way digital hats without checking
1977
 * whether they may be analog.
1978
 *
1979
 * The variable can be set to the following values:
1980
 *
1981
 * - "0": Only map hat axis inputs to digital hat outputs if the input axes
1982
 *   appear to actually be digital. (default)
1983
 * - "1": Always handle the input axes numbered ABS_HAT0X to ABS_HAT3Y as
1984
 *   digital hats.
1985
 *
1986
 * This hint should be set before a controller is opened.
1987
 *
1988
 * \since This hint is available since SDL 3.2.0.
1989
 */
1990
#define SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS "SDL_JOYSTICK_LINUX_DIGITAL_HATS"
1991
1992
/**
1993
 * A variable controlling whether digital hats on Linux will apply deadzones
1994
 * to their underlying input axes or use unfiltered values.
1995
 *
1996
 * The variable can be set to the following values:
1997
 *
1998
 * - "0": Return digital hat values based on unfiltered input axis values.
1999
 * - "1": Return digital hat values with deadzones on the input axes taken
2000
 *   into account. (default)
2001
 *
2002
 * This hint should be set before a controller is opened.
2003
 *
2004
 * \since This hint is available since SDL 3.2.0.
2005
 */
2006
#define SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES "SDL_JOYSTICK_LINUX_HAT_DEADZONES"
2007
2008
/**
2009
 * A variable controlling whether GCController should be used for controller
2010
 * handling.
2011
 *
2012
 * The variable can be set to the following values:
2013
 *
2014
 * - "0": GCController is not used.
2015
 * - "1": GCController is used. (default)
2016
 *
2017
 * This hint should be set before SDL is initialized.
2018
 *
2019
 * \since This hint is available since SDL 3.2.0.
2020
 */
2021
#define SDL_HINT_JOYSTICK_MFI "SDL_JOYSTICK_MFI"
2022
2023
/**
2024
 * A variable controlling whether the RAWINPUT joystick drivers should be used
2025
 * for better handling XInput-capable devices.
2026
 *
2027
 * The variable can be set to the following values:
2028
 *
2029
 * - "0": RAWINPUT drivers are not used. (default)
2030
 * - "1": RAWINPUT drivers are used.
2031
 *
2032
 * This hint should be set before SDL is initialized.
2033
 *
2034
 * \since This hint is available since SDL 3.2.0.
2035
 */
2036
#define SDL_HINT_JOYSTICK_RAWINPUT "SDL_JOYSTICK_RAWINPUT"
2037
2038
/**
2039
 * A variable controlling whether the RAWINPUT driver should pull correlated
2040
 * data from XInput.
2041
 *
2042
 * The variable can be set to the following values:
2043
 *
2044
 * - "0": RAWINPUT driver will only use data from raw input APIs.
2045
 * - "1": RAWINPUT driver will also pull data from XInput and
2046
 *   Windows.Gaming.Input, providing better trigger axes, guide button
2047
 *   presses, and rumble support for Xbox controllers. (default)
2048
 *
2049
 * This hint should be set before a gamepad is opened.
2050
 *
2051
 * \since This hint is available since SDL 3.2.0.
2052
 */
2053
#define SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT "SDL_JOYSTICK_RAWINPUT_CORRELATE_XINPUT"
2054
2055
/**
2056
 * A variable controlling whether the ROG Chakram mice should show up as
2057
 * joysticks.
2058
 *
2059
 * The variable can be set to the following values:
2060
 *
2061
 * - "0": ROG Chakram mice do not show up as joysticks. (default)
2062
 * - "1": ROG Chakram mice show up as joysticks.
2063
 *
2064
 * This hint should be set before SDL is initialized.
2065
 *
2066
 * \since This hint is available since SDL 3.2.0.
2067
 */
2068
#define SDL_HINT_JOYSTICK_ROG_CHAKRAM "SDL_JOYSTICK_ROG_CHAKRAM"
2069
2070
/**
2071
 * A variable controlling whether a separate thread should be used for
2072
 * handling joystick detection and raw input messages on Windows.
2073
 *
2074
 * The variable can be set to the following values:
2075
 *
2076
 * - "0": A separate thread is not used.
2077
 * - "1": A separate thread is used for handling raw input messages. (default)
2078
 *
2079
 * This hint should be set before SDL is initialized.
2080
 *
2081
 * \since This hint is available since SDL 3.2.0.
2082
 */
2083
#define SDL_HINT_JOYSTICK_THREAD "SDL_JOYSTICK_THREAD"
2084
2085
/**
2086
 * A variable containing a list of throttle style controllers.
2087
 *
2088
 * The format of the string is a comma separated list of USB VID/PID pairs in
2089
 * hexadecimal form, e.g.
2090
 *
2091
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
2092
 *
2093
 * The variable can also take the form of "@file", in which case the named
2094
 * file will be loaded and interpreted as the value of the variable.
2095
 *
2096
 * This hint can be set anytime.
2097
 *
2098
 * \since This hint is available since SDL 3.2.0.
2099
 */
2100
#define SDL_HINT_JOYSTICK_THROTTLE_DEVICES "SDL_JOYSTICK_THROTTLE_DEVICES"
2101
2102
/**
2103
 * A variable containing a list of devices that are not throttle style
2104
 * controllers.
2105
 *
2106
 * This will override SDL_HINT_JOYSTICK_THROTTLE_DEVICES and the built in
2107
 * device list.
2108
 *
2109
 * The format of the string is a comma separated list of USB VID/PID pairs in
2110
 * hexadecimal form, e.g.
2111
 *
2112
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
2113
 *
2114
 * The variable can also take the form of "@file", in which case the named
2115
 * file will be loaded and interpreted as the value of the variable.
2116
 *
2117
 * This hint can be set anytime.
2118
 *
2119
 * \since This hint is available since SDL 3.2.0.
2120
 */
2121
#define SDL_HINT_JOYSTICK_THROTTLE_DEVICES_EXCLUDED "SDL_JOYSTICK_THROTTLE_DEVICES_EXCLUDED"
2122
2123
/**
2124
 * A variable controlling whether Windows.Gaming.Input should be used for
2125
 * controller handling.
2126
 *
2127
 * The variable can be set to the following values:
2128
 *
2129
 * - "0": WGI is not used. (default)
2130
 * - "1": WGI is used.
2131
 *
2132
 * This hint should be set before SDL is initialized.
2133
 *
2134
 * \since This hint is available since SDL 3.2.0.
2135
 */
2136
#define SDL_HINT_JOYSTICK_WGI "SDL_JOYSTICK_WGI"
2137
2138
/**
2139
 * A variable containing a list of wheel style controllers.
2140
 *
2141
 * The format of the string is a comma separated list of USB VID/PID pairs in
2142
 * hexadecimal form, e.g.
2143
 *
2144
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
2145
 *
2146
 * The variable can also take the form of "@file", in which case the named
2147
 * file will be loaded and interpreted as the value of the variable.
2148
 *
2149
 * This hint can be set anytime.
2150
 *
2151
 * \since This hint is available since SDL 3.2.0.
2152
 */
2153
#define SDL_HINT_JOYSTICK_WHEEL_DEVICES "SDL_JOYSTICK_WHEEL_DEVICES"
2154
2155
/**
2156
 * A variable containing a list of devices that are not wheel style
2157
 * controllers.
2158
 *
2159
 * This will override SDL_HINT_JOYSTICK_WHEEL_DEVICES and the built in device
2160
 * list.
2161
 *
2162
 * The format of the string is a comma separated list of USB VID/PID pairs in
2163
 * hexadecimal form, e.g.
2164
 *
2165
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
2166
 *
2167
 * The variable can also take the form of "@file", in which case the named
2168
 * file will be loaded and interpreted as the value of the variable.
2169
 *
2170
 * This hint can be set anytime.
2171
 *
2172
 * \since This hint is available since SDL 3.2.0.
2173
 */
2174
#define SDL_HINT_JOYSTICK_WHEEL_DEVICES_EXCLUDED "SDL_JOYSTICK_WHEEL_DEVICES_EXCLUDED"
2175
2176
/**
2177
 * A variable containing a list of devices known to have all axes centered at
2178
 * zero.
2179
 *
2180
 * The format of the string is a comma separated list of USB VID/PID pairs in
2181
 * hexadecimal form, e.g.
2182
 *
2183
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
2184
 *
2185
 * The variable can also take the form of "@file", in which case the named
2186
 * file will be loaded and interpreted as the value of the variable.
2187
 *
2188
 * This hint should be set before a controller is opened.
2189
 *
2190
 * \since This hint is available since SDL 3.2.0.
2191
 */
2192
#define SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES "SDL_JOYSTICK_ZERO_CENTERED_DEVICES"
2193
2194
/**
2195
 * A variable containing a list of devices and their desired number of haptic
2196
 * (force feedback) enabled axis.
2197
 *
2198
 * The format of the string is a comma separated list of USB VID/PID pairs in
2199
 * hexadecimal form plus the number of desired axes, e.g.
2200
 *
2201
 * `0xAAAA/0xBBBB/1,0xCCCC/0xDDDD/3`
2202
 *
2203
 * This hint supports a "wildcard" device that will set the number of haptic
2204
 * axes on all initialized haptic devices which were not defined explicitly in
2205
 * this hint.
2206
 *
2207
 * `0xFFFF/0xFFFF/1`
2208
 *
2209
 * This hint should be set before a controller is opened. The number of haptic
2210
 * axes won't exceed the number of real axes found on the device.
2211
 *
2212
 * \since This hint is available since SDL 3.2.5.
2213
 */
2214
#define SDL_HINT_JOYSTICK_HAPTIC_AXES "SDL_JOYSTICK_HAPTIC_AXES"
2215
2216
/**
2217
 * A variable that controls keycode representation in keyboard events.
2218
 *
2219
 * This variable is a comma separated set of options for translating keycodes
2220
 * in events:
2221
 *
2222
 * - "none": Keycode options are cleared, this overrides other options.
2223
 * - "hide_numpad": The numpad keysyms will be translated into their
2224
 *   non-numpad versions based on the current NumLock state. For example,
2225
 *   SDLK_KP_4 would become SDLK_4 if SDL_KMOD_NUM is set in the event
2226
 *   modifiers, and SDLK_LEFT if it is unset.
2227
 * - "french_numbers": The number row on French keyboards is inverted, so
2228
 *   pressing the 1 key would yield the keycode SDLK_1, or '1', instead of
2229
 *   SDLK_AMPERSAND, or '&'
2230
 * - "latin_letters": For keyboards using non-Latin letters, such as Russian
2231
 *   or Thai, the letter keys generate keycodes as though it had an en_US
2232
 *   layout. e.g. pressing the key associated with SDL_SCANCODE_A on a Russian
2233
 *   keyboard would yield 'a' instead of a Cyrillic letter.
2234
 *
2235
 * The default value for this hint is "french_numbers,latin_letters"
2236
 *
2237
 * Some platforms like Emscripten only provide modified keycodes and the
2238
 * options are not used.
2239
 *
2240
 * These options do not affect the return value of SDL_GetKeyFromScancode() or
2241
 * SDL_GetScancodeFromKey(), they just apply to the keycode included in key
2242
 * events.
2243
 *
2244
 * This hint can be set anytime.
2245
 *
2246
 * \since This hint is available since SDL 3.2.0.
2247
 */
2248
#define SDL_HINT_KEYCODE_OPTIONS "SDL_KEYCODE_OPTIONS"
2249
2250
/**
2251
 * A variable that controls what KMSDRM device to use.
2252
 *
2253
 * SDL might open something like "/dev/dri/cardNN" to access KMSDRM
2254
 * functionality, where "NN" is a device index number. SDL makes a guess at
2255
 * the best index to use (usually zero), but the app or user can set this hint
2256
 * to a number between 0 and 99 to force selection.
2257
 *
2258
 * This hint should be set before SDL is initialized.
2259
 *
2260
 * \since This hint is available since SDL 3.2.0.
2261
 */
2262
#define SDL_HINT_KMSDRM_DEVICE_INDEX "SDL_KMSDRM_DEVICE_INDEX"
2263
2264
/**
2265
 * A variable that controls whether SDL requires DRM master access in order to
2266
 * initialize the KMSDRM video backend.
2267
 *
2268
 * The DRM subsystem has a concept of a "DRM master" which is a DRM client
2269
 * that has the ability to set planes, set cursor, etc. When SDL is DRM
2270
 * master, it can draw to the screen using the SDL rendering APIs. Without DRM
2271
 * master, SDL is still able to process input and query attributes of attached
2272
 * displays, but it cannot change display state or draw to the screen
2273
 * directly.
2274
 *
2275
 * In some cases, it can be useful to have the KMSDRM backend even if it
2276
 * cannot be used for rendering. An app may want to use SDL for input
2277
 * processing while using another rendering API (such as an MMAL overlay on
2278
 * Raspberry Pi) or using its own code to render to DRM overlays that SDL
2279
 * doesn't support.
2280
 *
2281
 * The variable can be set to the following values:
2282
 *
2283
 * - "0": SDL will allow usage of the KMSDRM backend without DRM master.
2284
 * - "1": SDL Will require DRM master to use the KMSDRM backend. (default)
2285
 *
2286
 * This hint should be set before SDL is initialized.
2287
 *
2288
 * \since This hint is available since SDL 3.2.0.
2289
 */
2290
#define SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER "SDL_KMSDRM_REQUIRE_DRM_MASTER"
2291
2292
/**
2293
 * A variable controlling the default SDL log levels.
2294
 *
2295
 * This variable is a comma separated set of category=level tokens that define
2296
 * the default logging levels for SDL applications.
2297
 *
2298
 * The category can be a numeric category, one of "app", "error", "assert",
2299
 * "system", "audio", "video", "render", "input", "test", or `*` for any
2300
 * unspecified category.
2301
 *
2302
 * The level can be a numeric level, one of "verbose", "debug", "info",
2303
 * "warn", "error", "critical", or "quiet" to disable that category.
2304
 *
2305
 * You can omit the category if you want to set the logging level for all
2306
 * categories.
2307
 *
2308
 * If this hint isn't set, the default log levels are equivalent to:
2309
 *
2310
 * `app=info,assert=warn,test=verbose,*=error`
2311
 *
2312
 * This hint can be set anytime.
2313
 *
2314
 * \since This hint is available since SDL 3.2.0.
2315
 */
2316
#define SDL_HINT_LOGGING "SDL_LOGGING"
2317
2318
/**
2319
 * A variable controlling whether to force the application to become the
2320
 * foreground process when launched on macOS.
2321
 *
2322
 * The variable can be set to the following values:
2323
 *
2324
 * - "0": The application is brought to the foreground when launched.
2325
 *   (default)
2326
 * - "1": The application may remain in the background when launched.
2327
 *
2328
 * This hint needs to be set before SDL_Init().
2329
 *
2330
 * \since This hint is available since SDL 3.2.0.
2331
 */
2332
#define SDL_HINT_MAC_BACKGROUND_APP "SDL_MAC_BACKGROUND_APP"
2333
2334
/**
2335
 * A variable that determines whether Ctrl+Click should generate a right-click
2336
 * event on macOS.
2337
 *
2338
 * The variable can be set to the following values:
2339
 *
2340
 * - "0": Ctrl+Click does not generate a right mouse button click event.
2341
 *   (default)
2342
 * - "1": Ctrl+Click generated a right mouse button click event.
2343
 *
2344
 * This hint can be set anytime.
2345
 *
2346
 * \since This hint is available since SDL 3.2.0.
2347
 */
2348
#define SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK "SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK"
2349
2350
/**
2351
 * A variable controlling whether dispatching OpenGL context updates should
2352
 * block the dispatching thread until the main thread finishes processing on
2353
 * macOS.
2354
 *
2355
 * The variable can be set to the following values:
2356
 *
2357
 * - "0": Dispatching OpenGL context updates will block the dispatching thread
2358
 *   until the main thread finishes processing. (default)
2359
 * - "1": Dispatching OpenGL context updates will allow the dispatching thread
2360
 *   to continue execution.
2361
 *
2362
 * Generally you want the default, but if you have OpenGL code in a background
2363
 * thread on a Mac, and the main thread hangs because it's waiting for that
2364
 * background thread, but that background thread is also hanging because it's
2365
 * waiting for the main thread to do an update, this might fix your issue.
2366
 *
2367
 * This hint can be set anytime.
2368
 *
2369
 * \since This hint is available since SDL 3.2.0.
2370
 */
2371
#define SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH "SDL_MAC_OPENGL_ASYNC_DISPATCH"
2372
2373
/**
2374
 * A variable controlling whether the Option key on macOS should be remapped
2375
 * to act as the Alt key.
2376
 *
2377
 * The variable can be set to the following values:
2378
 *
2379
 * - "none": The Option key is not remapped to Alt. (default)
2380
 * - "only_left": Only the left Option key is remapped to Alt.
2381
 * - "only_right": Only the right Option key is remapped to Alt.
2382
 * - "both": Both Option keys are remapped to Alt.
2383
 *
2384
 * This will prevent the triggering of key compositions that rely on the
2385
 * Option key, but will still send the Alt modifier for keyboard events. In
2386
 * the case that both Alt and Option are pressed, the Option key will be
2387
 * ignored. This is particularly useful for applications like terminal
2388
 * emulators and graphical user interfaces (GUIs) that rely on Alt key
2389
 * functionality for shortcuts or navigation. This does not apply to
2390
 * SDL_GetKeyFromScancode and only has an effect if IME is enabled.
2391
 *
2392
 * This hint can be set anytime.
2393
 *
2394
 * \since This hint is available since SDL 3.2.0.
2395
 */
2396
#define SDL_HINT_MAC_OPTION_AS_ALT "SDL_MAC_OPTION_AS_ALT"
2397
2398
/**
2399
 * A variable controlling whether SDL_EVENT_MOUSE_WHEEL event values will have
2400
 * momentum on macOS.
2401
 *
2402
 * The variable can be set to the following values:
2403
 *
2404
 * - "0": The mouse wheel events will have no momentum. (default)
2405
 * - "1": The mouse wheel events will have momentum.
2406
 *
2407
 * This hint needs to be set before SDL_Init().
2408
 *
2409
 * \since This hint is available since SDL 3.2.0.
2410
 */
2411
#define SDL_HINT_MAC_SCROLL_MOMENTUM "SDL_MAC_SCROLL_MOMENTUM"
2412
2413
/**
2414
 * Request SDL_AppIterate() be called at a specific rate.
2415
 *
2416
 * If this is set to a number, it represents Hz, so "60" means try to iterate
2417
 * 60 times per second. "0" means to iterate as fast as possible. Negative
2418
 * values are illegal, but reserved, in case they are useful in a future
2419
 * revision of SDL.
2420
 *
2421
 * There are other strings that have special meaning. If set to "waitevent",
2422
 * SDL_AppIterate will not be called until new event(s) have arrived (and been
2423
 * processed by SDL_AppEvent). This can be useful for apps that are completely
2424
 * idle except in response to input.
2425
 *
2426
 * On some platforms, or if you are using SDL_main instead of SDL_AppIterate,
2427
 * this hint is ignored. When the hint can be used, it is allowed to be
2428
 * changed at any time.
2429
 *
2430
 * This defaults to 0, and specifying NULL for the hint's value will restore
2431
 * the default.
2432
 *
2433
 * This hint can be set anytime.
2434
 *
2435
 * \since This hint is available since SDL 3.2.0.
2436
 */
2437
#define SDL_HINT_MAIN_CALLBACK_RATE "SDL_MAIN_CALLBACK_RATE"
2438
2439
/**
2440
 * A variable controlling whether the mouse is captured while mouse buttons
2441
 * are pressed.
2442
 *
2443
 * The variable can be set to the following values:
2444
 *
2445
 * - "0": The mouse is not captured while mouse buttons are pressed.
2446
 * - "1": The mouse is captured while mouse buttons are pressed.
2447
 *
2448
 * By default the mouse is captured while mouse buttons are pressed so if the
2449
 * mouse is dragged outside the window, the application continues to receive
2450
 * mouse events until the button is released.
2451
 *
2452
 * This hint can be set anytime.
2453
 *
2454
 * \since This hint is available since SDL 3.2.0.
2455
 */
2456
1
#define SDL_HINT_MOUSE_AUTO_CAPTURE "SDL_MOUSE_AUTO_CAPTURE"
2457
2458
/**
2459
 * A variable setting the double click radius, in pixels.
2460
 *
2461
 * This hint can be set anytime.
2462
 *
2463
 * \since This hint is available since SDL 3.2.0.
2464
 */
2465
#define SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS "SDL_MOUSE_DOUBLE_CLICK_RADIUS"
2466
2467
/**
2468
 * A variable setting the double click time, in milliseconds.
2469
 *
2470
 * This hint can be set anytime.
2471
 *
2472
 * \since This hint is available since SDL 3.2.0.
2473
 */
2474
#define SDL_HINT_MOUSE_DOUBLE_CLICK_TIME "SDL_MOUSE_DOUBLE_CLICK_TIME"
2475
2476
/**
2477
 * A variable setting which system cursor to use as the default cursor.
2478
 *
2479
 * This should be an integer corresponding to the SDL_SystemCursor enum. The
2480
 * default value is zero (SDL_SYSTEM_CURSOR_DEFAULT).
2481
 *
2482
 * This hint needs to be set before SDL_Init().
2483
 *
2484
 * \since This hint is available since SDL 3.2.0.
2485
 */
2486
#define SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR "SDL_MOUSE_DEFAULT_SYSTEM_CURSOR"
2487
2488
/**
2489
 * A variable controlling whether warping a hidden mouse cursor will activate
2490
 * relative mouse mode.
2491
 *
2492
 * When this hint is set, the mouse cursor is hidden, and multiple warps to
2493
 * the window center occur within a short time period, SDL will emulate mouse
2494
 * warps using relative mouse mode. This can provide smoother and more
2495
 * reliable mouse motion for some older games, which continuously calculate
2496
 * the distance travelled by the mouse pointer and warp it back to the center
2497
 * of the window, rather than using relative mouse motion.
2498
 *
2499
 * Note that relative mouse mode may have different mouse acceleration
2500
 * behavior than pointer warps.
2501
 *
2502
 * If your application needs to repeatedly warp the hidden mouse cursor at a
2503
 * high-frequency for other purposes, it should disable this hint.
2504
 *
2505
 * The variable can be set to the following values:
2506
 *
2507
 * - "0": Attempts to warp the mouse will always be made.
2508
 * - "1": Some mouse warps will be emulated by forcing relative mouse mode.
2509
 *   (default)
2510
 *
2511
 * If not set, this is automatically enabled unless an application uses
2512
 * relative mouse mode directly.
2513
 *
2514
 * This hint can be set anytime.
2515
 *
2516
 * \since This hint is available since SDL 3.2.0.
2517
 */
2518
#define SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE "SDL_MOUSE_EMULATE_WARP_WITH_RELATIVE"
2519
2520
/**
2521
 * Allow mouse click events when clicking to focus an SDL window.
2522
 *
2523
 * The variable can be set to the following values:
2524
 *
2525
 * - "0": Ignore mouse clicks that activate a window. (default)
2526
 * - "1": Generate events for mouse clicks that activate a window.
2527
 *
2528
 * This hint can be set anytime.
2529
 *
2530
 * \since This hint is available since SDL 3.2.0.
2531
 */
2532
1
#define SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH "SDL_MOUSE_FOCUS_CLICKTHROUGH"
2533
2534
/**
2535
 * A variable setting the speed scale for mouse motion, in floating point,
2536
 * when the mouse is not in relative mode.
2537
 *
2538
 * This hint can be set anytime.
2539
 *
2540
 * \since This hint is available since SDL 3.2.0.
2541
 */
2542
#define SDL_HINT_MOUSE_NORMAL_SPEED_SCALE "SDL_MOUSE_NORMAL_SPEED_SCALE"
2543
2544
/**
2545
 * A variable controlling whether relative mouse mode constrains the mouse to
2546
 * the center of the window.
2547
 *
2548
 * Constraining to the center of the window works better for FPS games and
2549
 * when the application is running over RDP. Constraining to the whole window
2550
 * works better for 2D games and increases the chance that the mouse will be
2551
 * in the correct position when using high DPI mice.
2552
 *
2553
 * The variable can be set to the following values:
2554
 *
2555
 * - "0": Relative mouse mode constrains the mouse to the window.
2556
 * - "1": Relative mouse mode constrains the mouse to the center of the
2557
 *   window. (default)
2558
 *
2559
 * This hint can be set anytime.
2560
 *
2561
 * \since This hint is available since SDL 3.2.0.
2562
 */
2563
#define SDL_HINT_MOUSE_RELATIVE_MODE_CENTER "SDL_MOUSE_RELATIVE_MODE_CENTER"
2564
2565
/**
2566
 * A variable setting the scale for mouse motion, in floating point, when the
2567
 * mouse is in relative mode.
2568
 *
2569
 * This hint can be set anytime.
2570
 *
2571
 * \since This hint is available since SDL 3.2.0.
2572
 */
2573
#define SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE "SDL_MOUSE_RELATIVE_SPEED_SCALE"
2574
2575
/**
2576
 * A variable controlling whether the system mouse acceleration curve is used
2577
 * for relative mouse motion.
2578
 *
2579
 * The variable can be set to the following values:
2580
 *
2581
 * - "0": Relative mouse motion will be unscaled. (default)
2582
 * - "1": Relative mouse motion will be scaled using the system mouse
2583
 *   acceleration curve.
2584
 *
2585
 * If SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE is set, that will be applied after
2586
 * system speed scale.
2587
 *
2588
 * This hint can be set anytime.
2589
 *
2590
 * \since This hint is available since SDL 3.2.0.
2591
 */
2592
#define SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE "SDL_MOUSE_RELATIVE_SYSTEM_SCALE"
2593
2594
/**
2595
 * A variable controlling whether a motion event should be generated for mouse
2596
 * warping in relative mode.
2597
 *
2598
 * The variable can be set to the following values:
2599
 *
2600
 * - "0": Warping the mouse will not generate a motion event in relative mode
2601
 * - "1": Warping the mouse will generate a motion event in relative mode
2602
 *
2603
 * By default warping the mouse will not generate motion events in relative
2604
 * mode. This avoids the application having to filter out large relative
2605
 * motion due to warping.
2606
 *
2607
 * This hint can be set anytime.
2608
 *
2609
 * \since This hint is available since SDL 3.2.0.
2610
 */
2611
#define SDL_HINT_MOUSE_RELATIVE_WARP_MOTION "SDL_MOUSE_RELATIVE_WARP_MOTION"
2612
2613
/**
2614
 * A variable controlling whether the hardware cursor stays visible when
2615
 * relative mode is active.
2616
 *
2617
 * This variable can be set to the following values:
2618
 *
2619
 * - "0": The cursor will be hidden while relative mode is active (default)
2620
 * - "1": The cursor will remain visible while relative mode is active
2621
 *
2622
 * Note that for systems without raw hardware inputs, relative mode is
2623
 * implemented using warping, so the hardware cursor will visibly warp between
2624
 * frames if this is enabled on those systems.
2625
 *
2626
 * This hint can be set anytime.
2627
 *
2628
 * \since This hint is available since SDL 3.2.0.
2629
 */
2630
#define SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE "SDL_MOUSE_RELATIVE_CURSOR_VISIBLE"
2631
2632
/**
2633
 * A variable controlling whether mouse events should generate synthetic touch
2634
 * events.
2635
 *
2636
 * The variable can be set to the following values:
2637
 *
2638
 * - "0": Mouse events will not generate touch events. (default for desktop
2639
 *   platforms)
2640
 * - "1": Mouse events will generate touch events. (default for mobile
2641
 *   platforms, such as Android and iOS)
2642
 *
2643
 * This hint can be set anytime.
2644
 *
2645
 * \since This hint is available since SDL 3.2.0.
2646
 */
2647
#define SDL_HINT_MOUSE_TOUCH_EVENTS "SDL_MOUSE_TOUCH_EVENTS"
2648
2649
/**
2650
 * A variable controlling whether the keyboard should be muted on the console.
2651
 *
2652
 * Normally the keyboard is muted while SDL applications are running so that
2653
 * keyboard input doesn't show up as key strokes on the console. This hint
2654
 * allows you to turn that off for debugging purposes.
2655
 *
2656
 * The variable can be set to the following values:
2657
 *
2658
 * - "0": Allow keystrokes to go through to the console.
2659
 * - "1": Mute keyboard input so it doesn't show up on the console. (default)
2660
 *
2661
 * This hint should be set before SDL is initialized.
2662
 *
2663
 * \since This hint is available since SDL 3.2.0.
2664
 */
2665
#define SDL_HINT_MUTE_CONSOLE_KEYBOARD "SDL_MUTE_CONSOLE_KEYBOARD"
2666
2667
/**
2668
 * Tell SDL not to catch the SIGINT or SIGTERM signals on POSIX platforms.
2669
 *
2670
 * The variable can be set to the following values:
2671
 *
2672
 * - "0": SDL will install a SIGINT and SIGTERM handler, and when it catches a
2673
 *   signal, convert it into an SDL_EVENT_QUIT event. (default)
2674
 * - "1": SDL will not install a signal handler at all.
2675
 *
2676
 * This hint should be set before SDL is initialized.
2677
 *
2678
 * \since This hint is available since SDL 3.2.0.
2679
 */
2680
#define SDL_HINT_NO_SIGNAL_HANDLERS "SDL_NO_SIGNAL_HANDLERS"
2681
2682
/**
2683
 * Specify the OpenGL library to load.
2684
 *
2685
 * This hint should be set before creating an OpenGL window or creating an
2686
 * OpenGL context. If this hint isn't set, SDL will choose a reasonable
2687
 * default.
2688
 *
2689
 * \since This hint is available since SDL 3.2.0.
2690
 */
2691
#define SDL_HINT_OPENGL_LIBRARY "SDL_OPENGL_LIBRARY"
2692
2693
/**
2694
 * Specify the EGL library to load.
2695
 *
2696
 * This hint should be set before creating an OpenGL window or creating an
2697
 * OpenGL context. This hint is only considered if SDL is using EGL to manage
2698
 * OpenGL contexts. If this hint isn't set, SDL will choose a reasonable
2699
 * default.
2700
 *
2701
 * \since This hint is available since SDL 3.2.0.
2702
 */
2703
#define SDL_HINT_EGL_LIBRARY "SDL_EGL_LIBRARY"
2704
2705
/**
2706
 * A variable controlling what driver to use for OpenGL ES contexts.
2707
 *
2708
 * On some platforms, currently Windows and X11, OpenGL drivers may support
2709
 * creating contexts with an OpenGL ES profile. By default SDL uses these
2710
 * profiles, when available, otherwise it attempts to load an OpenGL ES
2711
 * library, e.g. that provided by the ANGLE project. This variable controls
2712
 * whether SDL follows this default behaviour or will always load an OpenGL ES
2713
 * library.
2714
 *
2715
 * Circumstances where this is useful include - Testing an app with a
2716
 * particular OpenGL ES implementation, e.g ANGLE, or emulator, e.g. those
2717
 * from ARM, Imagination or Qualcomm. - Resolving OpenGL ES function addresses
2718
 * at link time by linking with the OpenGL ES library instead of querying them
2719
 * at run time with SDL_GL_GetProcAddress().
2720
 *
2721
 * Caution: for an application to work with the default behaviour across
2722
 * different OpenGL drivers it must query the OpenGL ES function addresses at
2723
 * run time using SDL_GL_GetProcAddress().
2724
 *
2725
 * This variable is ignored on most platforms because OpenGL ES is native or
2726
 * not supported.
2727
 *
2728
 * The variable can be set to the following values:
2729
 *
2730
 * - "0": Use ES profile of OpenGL, if available. (default)
2731
 * - "1": Load OpenGL ES library using the default library names.
2732
 *
2733
 * This hint should be set before SDL is initialized.
2734
 *
2735
 * \since This hint is available since SDL 3.2.0.
2736
 */
2737
#define SDL_HINT_OPENGL_ES_DRIVER "SDL_OPENGL_ES_DRIVER"
2738
2739
/**
2740
 * Mechanism to specify openvr_api library location
2741
 *
2742
 * By default, when using the OpenVR driver, it will search for the API
2743
 * library in the current folder. But, if you wish to use a system API you can
2744
 * specify that by using this hint. This should be the full or relative path
2745
 * to a .dll on Windows or .so on Linux.
2746
 *
2747
 * \since This hint is available since SDL 3.2.0.
2748
 */
2749
#define SDL_HINT_OPENVR_LIBRARY              "SDL_OPENVR_LIBRARY"
2750
2751
/**
2752
 * A variable controlling which orientations are allowed on iOS/Android.
2753
 *
2754
 * In some circumstances it is necessary to be able to explicitly control
2755
 * which UI orientations are allowed.
2756
 *
2757
 * This variable is a space delimited list of the following values:
2758
 *
2759
 * - "LandscapeLeft"
2760
 * - "LandscapeRight"
2761
 * - "Portrait"
2762
 * - "PortraitUpsideDown"
2763
 *
2764
 * This hint should be set before SDL is initialized.
2765
 *
2766
 * \since This hint is available since SDL 3.2.0.
2767
 */
2768
#define SDL_HINT_ORIENTATIONS "SDL_ORIENTATIONS"
2769
2770
/**
2771
 * A variable controlling the use of a sentinel event when polling the event
2772
 * queue.
2773
 *
2774
 * When polling for events, SDL_PumpEvents is used to gather new events from
2775
 * devices. If a device keeps producing new events between calls to
2776
 * SDL_PumpEvents, a poll loop will become stuck until the new events stop.
2777
 * This is most noticeable when moving a high frequency mouse.
2778
 *
2779
 * The variable can be set to the following values:
2780
 *
2781
 * - "0": Disable poll sentinels.
2782
 * - "1": Enable poll sentinels. (default)
2783
 *
2784
 * This hint can be set anytime.
2785
 *
2786
 * \since This hint is available since SDL 3.2.0.
2787
 */
2788
#define SDL_HINT_POLL_SENTINEL "SDL_POLL_SENTINEL"
2789
2790
/**
2791
 * Override for SDL_GetPreferredLocales().
2792
 *
2793
 * If set, this will be favored over anything the OS might report for the
2794
 * user's preferred locales. Changing this hint at runtime will not generate a
2795
 * SDL_EVENT_LOCALE_CHANGED event (but if you can change the hint, you can
2796
 * push your own event, if you want).
2797
 *
2798
 * The format of this hint is a comma-separated list of language and locale,
2799
 * combined with an underscore, as is a common format: "en_GB". Locale is
2800
 * optional: "en". So you might have a list like this: "en_GB,jp,es_PT"
2801
 *
2802
 * This hint can be set anytime.
2803
 *
2804
 * \since This hint is available since SDL 3.2.0.
2805
 */
2806
#define SDL_HINT_PREFERRED_LOCALES "SDL_PREFERRED_LOCALES"
2807
2808
/**
2809
 * A variable that decides whether to send SDL_EVENT_QUIT when closing the
2810
 * last window.
2811
 *
2812
 * The variable can be set to the following values:
2813
 *
2814
 * - "0": SDL will not send an SDL_EVENT_QUIT event when the last window is
2815
 *   requesting to close. Note that in this case, there are still other
2816
 *   legitimate reasons one might get an SDL_EVENT_QUIT event: choosing "Quit"
2817
 *   from the macOS menu bar, sending a SIGINT (ctrl-c) on Unix, etc.
2818
 * - "1": SDL will send a quit event when the last window is requesting to
2819
 *   close. (default)
2820
 *
2821
 * If there is at least one active system tray icon, SDL_EVENT_QUIT will
2822
 * instead be sent when both the last window will be closed and the last tray
2823
 * icon will be destroyed.
2824
 *
2825
 * This hint can be set anytime.
2826
 *
2827
 * \since This hint is available since SDL 3.2.0.
2828
 */
2829
#define SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE "SDL_QUIT_ON_LAST_WINDOW_CLOSE"
2830
2831
/**
2832
 * A variable controlling whether the Direct3D device is initialized for
2833
 * thread-safe operations.
2834
 *
2835
 * The variable can be set to the following values:
2836
 *
2837
 * - "0": Thread-safety is not enabled. (default)
2838
 * - "1": Thread-safety is enabled.
2839
 *
2840
 * This hint should be set before creating a renderer.
2841
 *
2842
 * \since This hint is available since SDL 3.2.0.
2843
 */
2844
#define SDL_HINT_RENDER_DIRECT3D_THREADSAFE "SDL_RENDER_DIRECT3D_THREADSAFE"
2845
2846
/**
2847
 * A variable controlling whether to enable Direct3D 11+'s Debug Layer.
2848
 *
2849
 * This variable does not have any effect on the Direct3D 9 based renderer.
2850
 *
2851
 * The variable can be set to the following values:
2852
 *
2853
 * - "0": Disable Debug Layer use. (default)
2854
 * - "1": Enable Debug Layer use.
2855
 *
2856
 * This hint should be set before creating a renderer.
2857
 *
2858
 * \since This hint is available since SDL 3.2.0.
2859
 */
2860
#define SDL_HINT_RENDER_DIRECT3D11_DEBUG "SDL_RENDER_DIRECT3D11_DEBUG"
2861
2862
/**
2863
 * A variable controlling whether to enable Vulkan Validation Layers.
2864
 *
2865
 * This variable can be set to the following values:
2866
 *
2867
 * - "0": Disable Validation Layer use
2868
 * - "1": Enable Validation Layer use
2869
 *
2870
 * By default, SDL does not use Vulkan Validation Layers.
2871
 *
2872
 * \since This hint is available since SDL 3.2.0.
2873
 */
2874
#define SDL_HINT_RENDER_VULKAN_DEBUG "SDL_RENDER_VULKAN_DEBUG"
2875
2876
/**
2877
 * A variable controlling whether to create the GPU device in debug mode.
2878
 *
2879
 * This variable can be set to the following values:
2880
 *
2881
 * - "0": Disable debug mode use (default)
2882
 * - "1": Enable debug mode use
2883
 *
2884
 * This hint should be set before creating a renderer.
2885
 *
2886
 * \since This hint is available since SDL 3.2.0.
2887
 */
2888
#define SDL_HINT_RENDER_GPU_DEBUG "SDL_RENDER_GPU_DEBUG"
2889
2890
/**
2891
 * A variable controlling whether to prefer a low-power GPU on multi-GPU
2892
 * systems.
2893
 *
2894
 * This variable can be set to the following values:
2895
 *
2896
 * - "0": Prefer high-performance GPU (default)
2897
 * - "1": Prefer low-power GPU
2898
 *
2899
 * This hint should be set before creating a renderer.
2900
 *
2901
 * \since This hint is available since SDL 3.2.0.
2902
 */
2903
#define SDL_HINT_RENDER_GPU_LOW_POWER "SDL_RENDER_GPU_LOW_POWER"
2904
2905
/**
2906
 * A variable specifying which render driver to use.
2907
 *
2908
 * If the application doesn't pick a specific renderer to use, this variable
2909
 * specifies the name of the preferred renderer. If the preferred renderer
2910
 * can't be initialized, creating a renderer will fail.
2911
 *
2912
 * This variable is case insensitive and can be set to the following values:
2913
 *
2914
 * - "direct3d"
2915
 * - "direct3d11"
2916
 * - "direct3d12"
2917
 * - "opengl"
2918
 * - "opengles2"
2919
 * - "opengles"
2920
 * - "metal"
2921
 * - "vulkan"
2922
 * - "gpu"
2923
 * - "software"
2924
 *
2925
 * This hint accepts a comma-separated list of driver names, and each will be
2926
 * tried in the order listed when creating a renderer until one succeeds or
2927
 * all of them fail.
2928
 *
2929
 * The default varies by platform, but it's the first one in the list that is
2930
 * available on the current platform.
2931
 *
2932
 * This hint should be set before creating a renderer.
2933
 *
2934
 * \since This hint is available since SDL 3.2.0.
2935
 */
2936
#define SDL_HINT_RENDER_DRIVER "SDL_RENDER_DRIVER"
2937
2938
/**
2939
 * A variable controlling how the 2D render API renders lines.
2940
 *
2941
 * The variable can be set to the following values:
2942
 *
2943
 * - "0": Use the default line drawing method (Bresenham's line algorithm)
2944
 * - "1": Use the driver point API using Bresenham's line algorithm (correct,
2945
 *   draws many points)
2946
 * - "2": Use the driver line API (occasionally misses line endpoints based on
2947
 *   hardware driver quirks
2948
 * - "3": Use the driver geometry API (correct, draws thicker diagonal lines)
2949
 *
2950
 * This hint should be set before creating a renderer.
2951
 *
2952
 * \since This hint is available since SDL 3.2.0.
2953
 */
2954
#define SDL_HINT_RENDER_LINE_METHOD "SDL_RENDER_LINE_METHOD"
2955
2956
/**
2957
 * A variable controlling whether the Metal render driver select low power
2958
 * device over default one.
2959
 *
2960
 * The variable can be set to the following values:
2961
 *
2962
 * - "0": Use the preferred OS device. (default)
2963
 * - "1": Select a low power device.
2964
 *
2965
 * This hint should be set before creating a renderer.
2966
 *
2967
 * \since This hint is available since SDL 3.2.0.
2968
 */
2969
#define SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE "SDL_RENDER_METAL_PREFER_LOW_POWER_DEVICE"
2970
2971
/**
2972
 * A variable controlling whether updates to the SDL screen surface should be
2973
 * synchronized with the vertical refresh, to avoid tearing.
2974
 *
2975
 * This hint overrides the application preference when creating a renderer.
2976
 *
2977
 * The variable can be set to the following values:
2978
 *
2979
 * - "0": Disable vsync. (default)
2980
 * - "1": Enable vsync.
2981
 *
2982
 * This hint should be set before creating a renderer.
2983
 *
2984
 * \since This hint is available since SDL 3.2.0.
2985
 */
2986
#define SDL_HINT_RENDER_VSYNC "SDL_RENDER_VSYNC"
2987
2988
/**
2989
 * A variable to control whether the return key on the soft keyboard should
2990
 * hide the soft keyboard on Android and iOS.
2991
 *
2992
 * This hint sets the default value of SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN.
2993
 *
2994
 * The variable can be set to the following values:
2995
 *
2996
 * - "0": The return key will be handled as a key event. (default)
2997
 * - "1": The return key will hide the keyboard.
2998
 *
2999
 * This hint can be set anytime.
3000
 *
3001
 * \since This hint is available since SDL 3.2.0.
3002
 */
3003
#define SDL_HINT_RETURN_KEY_HIDES_IME "SDL_RETURN_KEY_HIDES_IME"
3004
3005
/**
3006
 * A variable containing a list of ROG gamepad capable mice.
3007
 *
3008
 * The format of the string is a comma separated list of USB VID/PID pairs in
3009
 * hexadecimal form, e.g.
3010
 *
3011
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
3012
 *
3013
 * The variable can also take the form of "@file", in which case the named
3014
 * file will be loaded and interpreted as the value of the variable.
3015
 *
3016
 * This hint should be set before SDL is initialized.
3017
 *
3018
 * \since This hint is available since SDL 3.2.0.
3019
 *
3020
 * \sa SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED
3021
 */
3022
#define SDL_HINT_ROG_GAMEPAD_MICE "SDL_ROG_GAMEPAD_MICE"
3023
3024
/**
3025
 * A variable containing a list of devices that are not ROG gamepad capable
3026
 * mice.
3027
 *
3028
 * This will override SDL_HINT_ROG_GAMEPAD_MICE and the built in device list.
3029
 *
3030
 * The format of the string is a comma separated list of USB VID/PID pairs in
3031
 * hexadecimal form, e.g.
3032
 *
3033
 * `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
3034
 *
3035
 * The variable can also take the form of "@file", in which case the named
3036
 * file will be loaded and interpreted as the value of the variable.
3037
 *
3038
 * This hint should be set before SDL is initialized.
3039
 *
3040
 * \since This hint is available since SDL 3.2.0.
3041
 */
3042
#define SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED "SDL_ROG_GAMEPAD_MICE_EXCLUDED"
3043
3044
/**
3045
 * A variable controlling which Dispmanx layer to use on a Raspberry PI.
3046
 *
3047
 * Also known as Z-order. The variable can take a negative or positive value.
3048
 * The default is 10000.
3049
 *
3050
 * This hint should be set before SDL is initialized.
3051
 *
3052
 * \since This hint is available since SDL 3.2.0.
3053
 */
3054
#define SDL_HINT_RPI_VIDEO_LAYER "SDL_RPI_VIDEO_LAYER"
3055
3056
/**
3057
 * Specify an "activity name" for screensaver inhibition.
3058
 *
3059
 * Some platforms, notably Linux desktops, list the applications which are
3060
 * inhibiting the screensaver or other power-saving features.
3061
 *
3062
 * This hint lets you specify the "activity name" sent to the OS when
3063
 * SDL_DisableScreenSaver() is used (or the screensaver is automatically
3064
 * disabled). The contents of this hint are used when the screensaver is
3065
 * disabled. You should use a string that describes what your program is doing
3066
 * (and, therefore, why the screensaver is disabled). For example, "Playing a
3067
 * game" or "Watching a video".
3068
 *
3069
 * Setting this to "" or leaving it unset will have SDL use a reasonable
3070
 * default: "Playing a game" or something similar.
3071
 *
3072
 * This hint should be set before calling SDL_DisableScreenSaver()
3073
 *
3074
 * \since This hint is available since SDL 3.2.0.
3075
 */
3076
#define SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME "SDL_SCREENSAVER_INHIBIT_ACTIVITY_NAME"
3077
3078
/**
3079
 * A variable controlling whether SDL calls dbus_shutdown() on quit.
3080
 *
3081
 * This is useful as a debug tool to validate memory leaks, but shouldn't ever
3082
 * be set in production applications, as other libraries used by the
3083
 * application might use dbus under the hood and this can cause crashes if
3084
 * they continue after SDL_Quit().
3085
 *
3086
 * The variable can be set to the following values:
3087
 *
3088
 * - "0": SDL will not call dbus_shutdown() on quit. (default)
3089
 * - "1": SDL will call dbus_shutdown() on quit.
3090
 *
3091
 * This hint can be set anytime.
3092
 *
3093
 * \since This hint is available since SDL 3.2.0.
3094
 */
3095
#define SDL_HINT_SHUTDOWN_DBUS_ON_QUIT "SDL_SHUTDOWN_DBUS_ON_QUIT"
3096
3097
/**
3098
 * A variable that specifies a backend to use for title storage.
3099
 *
3100
 * By default, SDL will try all available storage backends in a reasonable
3101
 * order until it finds one that can work, but this hint allows the app or
3102
 * user to force a specific target, such as "pc" if, say, you are on Steam but
3103
 * want to avoid SteamRemoteStorage for title data.
3104
 *
3105
 * This hint should be set before SDL is initialized.
3106
 *
3107
 * \since This hint is available since SDL 3.2.0.
3108
 */
3109
#define SDL_HINT_STORAGE_TITLE_DRIVER "SDL_STORAGE_TITLE_DRIVER"
3110
3111
/**
3112
 * A variable that specifies a backend to use for user storage.
3113
 *
3114
 * By default, SDL will try all available storage backends in a reasonable
3115
 * order until it finds one that can work, but this hint allows the app or
3116
 * user to force a specific target, such as "pc" if, say, you are on Steam but
3117
 * want to avoid SteamRemoteStorage for user data.
3118
 *
3119
 * This hint should be set before SDL is initialized.
3120
 *
3121
 * \since This hint is available since SDL 3.2.0.
3122
 */
3123
#define SDL_HINT_STORAGE_USER_DRIVER "SDL_STORAGE_USER_DRIVER"
3124
3125
/**
3126
 * Specifies whether SDL_THREAD_PRIORITY_TIME_CRITICAL should be treated as
3127
 * realtime.
3128
 *
3129
 * On some platforms, like Linux, a realtime priority thread may be subject to
3130
 * restrictions that require special handling by the application. This hint
3131
 * exists to let SDL know that the app is prepared to handle said
3132
 * restrictions.
3133
 *
3134
 * On Linux, SDL will apply the following configuration to any thread that
3135
 * becomes realtime:
3136
 *
3137
 * - The SCHED_RESET_ON_FORK bit will be set on the scheduling policy,
3138
 * - An RLIMIT_RTTIME budget will be configured to the rtkit specified limit.
3139
 * - Exceeding this limit will result in the kernel sending SIGKILL to the
3140
 *   app, refer to the man pages for more information.
3141
 *
3142
 * The variable can be set to the following values:
3143
 *
3144
 * - "0": default platform specific behaviour
3145
 * - "1": Force SDL_THREAD_PRIORITY_TIME_CRITICAL to a realtime scheduling
3146
 *   policy
3147
 *
3148
 * This hint should be set before calling SDL_SetCurrentThreadPriority()
3149
 *
3150
 * \since This hint is available since SDL 3.2.0.
3151
 */
3152
#define SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL "SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL"
3153
3154
/**
3155
 * A string specifying additional information to use with
3156
 * SDL_SetCurrentThreadPriority.
3157
 *
3158
 * By default SDL_SetCurrentThreadPriority will make appropriate system
3159
 * changes in order to apply a thread priority. For example on systems using
3160
 * pthreads the scheduler policy is changed automatically to a policy that
3161
 * works well with a given priority. Code which has specific requirements can
3162
 * override SDL's default behavior with this hint.
3163
 *
3164
 * pthread hint values are "current", "other", "fifo" and "rr". Currently no
3165
 * other platform hint values are defined but may be in the future.
3166
 *
3167
 * On Linux, the kernel may send SIGKILL to realtime tasks which exceed the
3168
 * distro configured execution budget for rtkit. This budget can be queried
3169
 * through RLIMIT_RTTIME after calling SDL_SetCurrentThreadPriority().
3170
 *
3171
 * This hint should be set before calling SDL_SetCurrentThreadPriority()
3172
 *
3173
 * \since This hint is available since SDL 3.2.0.
3174
 */
3175
#define SDL_HINT_THREAD_PRIORITY_POLICY "SDL_THREAD_PRIORITY_POLICY"
3176
3177
/**
3178
 * A variable that controls the timer resolution, in milliseconds.
3179
 *
3180
 * The higher resolution the timer, the more frequently the CPU services timer
3181
 * interrupts, and the more precise delays are, but this takes up power and
3182
 * CPU time. This hint is only used on Windows.
3183
 *
3184
 * See this blog post for more information:
3185
 * http://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/
3186
 *
3187
 * The default value is "1".
3188
 *
3189
 * If this variable is set to "0", the system timer resolution is not set.
3190
 *
3191
 * This hint can be set anytime.
3192
 *
3193
 * \since This hint is available since SDL 3.2.0.
3194
 */
3195
#define SDL_HINT_TIMER_RESOLUTION "SDL_TIMER_RESOLUTION"
3196
3197
/**
3198
 * A variable controlling whether touch events should generate synthetic mouse
3199
 * events.
3200
 *
3201
 * The variable can be set to the following values:
3202
 *
3203
 * - "0": Touch events will not generate mouse events.
3204
 * - "1": Touch events will generate mouse events. (default)
3205
 *
3206
 * This hint can be set anytime.
3207
 *
3208
 * \since This hint is available since SDL 3.2.0.
3209
 */
3210
#define SDL_HINT_TOUCH_MOUSE_EVENTS "SDL_TOUCH_MOUSE_EVENTS"
3211
3212
/**
3213
 * A variable controlling whether trackpads should be treated as touch
3214
 * devices.
3215
 *
3216
 * On macOS (and possibly other platforms in the future), SDL will report
3217
 * touches on a trackpad as mouse input, which is generally what users expect
3218
 * from this device; however, these are often actually full multitouch-capable
3219
 * touch devices, so it might be preferable to some apps to treat them as
3220
 * such.
3221
 *
3222
 * The variable can be set to the following values:
3223
 *
3224
 * - "0": Trackpad will send mouse events. (default)
3225
 * - "1": Trackpad will send touch events.
3226
 *
3227
 * This hint should be set before SDL is initialized.
3228
 *
3229
 * \since This hint is available since SDL 3.2.0.
3230
 */
3231
#define SDL_HINT_TRACKPAD_IS_TOUCH_ONLY "SDL_TRACKPAD_IS_TOUCH_ONLY"
3232
3233
/**
3234
 * A variable controlling whether the Android / tvOS remotes should be listed
3235
 * as joystick devices, instead of sending keyboard events.
3236
 *
3237
 * The variable can be set to the following values:
3238
 *
3239
 * - "0": Remotes send enter/escape/arrow key events.
3240
 * - "1": Remotes are available as 2 axis, 2 button joysticks. (default)
3241
 *
3242
 * This hint should be set before SDL is initialized.
3243
 *
3244
 * \since This hint is available since SDL 3.2.0.
3245
 */
3246
#define SDL_HINT_TV_REMOTE_AS_JOYSTICK "SDL_TV_REMOTE_AS_JOYSTICK"
3247
3248
/**
3249
 * A variable controlling whether the screensaver is enabled.
3250
 *
3251
 * The variable can be set to the following values:
3252
 *
3253
 * - "0": Disable screensaver. (default)
3254
 * - "1": Enable screensaver.
3255
 *
3256
 * This hint should be set before SDL is initialized.
3257
 *
3258
 * \since This hint is available since SDL 3.2.0.
3259
 */
3260
#define SDL_HINT_VIDEO_ALLOW_SCREENSAVER "SDL_VIDEO_ALLOW_SCREENSAVER"
3261
3262
/**
3263
 * A comma separated list containing the names of the displays that SDL should
3264
 * sort to the front of the display list.
3265
 *
3266
 * When this hint is set, displays with matching name strings will be
3267
 * prioritized in the list of displays, as exposed by calling
3268
 * SDL_GetDisplays(), with the first listed becoming the primary display. The
3269
 * naming convention can vary depending on the environment, but it is usually
3270
 * a connector name (e.g. 'DP-1', 'DP-2', 'HDMI-A-1',etc...).
3271
 *
3272
 * On Wayland and X11 desktops, the connector names associated with displays
3273
 * can typically be found by using the `xrandr` utility.
3274
 *
3275
 * This hint is currently supported on the following drivers:
3276
 *
3277
 * - KMSDRM (kmsdrm)
3278
 * - Wayland (wayland)
3279
 * - X11 (x11)
3280
 *
3281
 * This hint should be set before SDL is initialized.
3282
 *
3283
 * \since This hint is available since SDL 3.2.0.
3284
 */
3285
#define SDL_HINT_VIDEO_DISPLAY_PRIORITY "SDL_VIDEO_DISPLAY_PRIORITY"
3286
3287
/**
3288
 * Tell the video driver that we only want a double buffer.
3289
 *
3290
 * By default, most lowlevel 2D APIs will use a triple buffer scheme that
3291
 * wastes no CPU time on waiting for vsync after issuing a flip, but
3292
 * introduces a frame of latency. On the other hand, using a double buffer
3293
 * scheme instead is recommended for cases where low latency is an important
3294
 * factor because we save a whole frame of latency.
3295
 *
3296
 * We do so by waiting for vsync immediately after issuing a flip, usually
3297
 * just after eglSwapBuffers call in the backend's *_SwapWindow function.
3298
 *
3299
 * This hint is currently supported on the following drivers:
3300
 *
3301
 * - Raspberry Pi (raspberrypi)
3302
 * - Wayland (wayland)
3303
 *
3304
 * This hint should be set before SDL is initialized.
3305
 *
3306
 * \since This hint is available since SDL 3.2.0.
3307
 */
3308
#define SDL_HINT_VIDEO_DOUBLE_BUFFER "SDL_VIDEO_DOUBLE_BUFFER"
3309
3310
/**
3311
 * A variable that specifies a video backend to use.
3312
 *
3313
 * By default, SDL will try all available video backends in a reasonable order
3314
 * until it finds one that can work, but this hint allows the app or user to
3315
 * force a specific target, such as "x11" if, say, you are on Wayland but want
3316
 * to try talking to the X server instead.
3317
 *
3318
 * This hint accepts a comma-separated list of driver names, and each will be
3319
 * tried in the order listed during init, until one succeeds or all of them
3320
 * fail.
3321
 *
3322
 * This hint should be set before SDL is initialized.
3323
 *
3324
 * \since This hint is available since SDL 3.2.0.
3325
 */
3326
#define SDL_HINT_VIDEO_DRIVER "SDL_VIDEO_DRIVER"
3327
3328
/**
3329
 * A variable controlling whether the dummy video driver saves output frames.
3330
 *
3331
 * - "0": Video frames are not saved to disk. (default)
3332
 * - "1": Video frames are saved to files in the format "SDL_windowX-Y.bmp",
3333
 *   where X is the window ID, and Y is the frame number.
3334
 *
3335
 * This hint can be set anytime.
3336
 *
3337
 * \since This hint is available since SDL 3.2.0.
3338
 */
3339
#define SDL_HINT_VIDEO_DUMMY_SAVE_FRAMES "SDL_VIDEO_DUMMY_SAVE_FRAMES"
3340
3341
/**
3342
 * If eglGetPlatformDisplay fails, fall back to calling eglGetDisplay.
3343
 *
3344
 * The variable can be set to one of the following values:
3345
 *
3346
 * - "0": Do not fall back to eglGetDisplay.
3347
 * - "1": Fall back to eglGetDisplay if eglGetPlatformDisplay fails. (default)
3348
 *
3349
 * This hint should be set before SDL is initialized.
3350
 *
3351
 * \since This hint is available since SDL 3.2.0.
3352
 */
3353
#define SDL_HINT_VIDEO_EGL_ALLOW_GETDISPLAY_FALLBACK "SDL_VIDEO_EGL_ALLOW_GETDISPLAY_FALLBACK"
3354
3355
/**
3356
 * A variable controlling whether the OpenGL context should be created with
3357
 * EGL.
3358
 *
3359
 * The variable can be set to the following values:
3360
 *
3361
 * - "0": Use platform-specific GL context creation API (GLX, WGL, CGL, etc).
3362
 *   (default)
3363
 * - "1": Use EGL
3364
 *
3365
 * This hint should be set before SDL is initialized.
3366
 *
3367
 * \since This hint is available since SDL 3.2.0.
3368
 */
3369
#define SDL_HINT_VIDEO_FORCE_EGL "SDL_VIDEO_FORCE_EGL"
3370
3371
/**
3372
 * A variable that specifies the policy for fullscreen Spaces on macOS.
3373
 *
3374
 * The variable can be set to the following values:
3375
 *
3376
 * - "0": Disable Spaces support (FULLSCREEN_DESKTOP won't use them and
3377
 *   SDL_WINDOW_RESIZABLE windows won't offer the "fullscreen" button on their
3378
 *   titlebars).
3379
 * - "1": Enable Spaces support (FULLSCREEN_DESKTOP will use them and
3380
 *   SDL_WINDOW_RESIZABLE windows will offer the "fullscreen" button on their
3381
 *   titlebars). (default)
3382
 *
3383
 * This hint should be set before creating a window.
3384
 *
3385
 * \since This hint is available since SDL 3.2.0.
3386
 */
3387
#define SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES "SDL_VIDEO_MAC_FULLSCREEN_SPACES"
3388
3389
/**
3390
 * A variable that specifies the menu visibility when a window is fullscreen
3391
 * in Spaces on macOS.
3392
 *
3393
 * The variable can be set to the following values:
3394
 *
3395
 * - "0": The menu will be hidden when the window is in a fullscreen space,
3396
 *   and not accessible by moving the mouse to the top of the screen.
3397
 * - "1": The menu will be accessible when the window is in a fullscreen
3398
 *   space.
3399
 * - "auto": The menu will be hidden if fullscreen mode was toggled on
3400
 *   programmatically via `SDL_SetWindowFullscreen()`, and accessible if
3401
 *   fullscreen was entered via the "fullscreen" button on the window title
3402
 *   bar. (default)
3403
 *
3404
 * This hint can be set anytime.
3405
 *
3406
 * \since This hint is available since SDL 3.2.0.
3407
 */
3408
#define SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY "SDL_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY"
3409
3410
/**
3411
 * A variable controlling whether fullscreen windows are minimized when they
3412
 * lose focus.
3413
 *
3414
 * The variable can be set to the following values:
3415
 *
3416
 * - "0": Fullscreen windows will not be minimized when they lose focus.
3417
 *   (default)
3418
 * - "1": Fullscreen windows are minimized when they lose focus.
3419
 *
3420
 * This hint can be set anytime.
3421
 *
3422
 * \since This hint is available since SDL 3.2.0.
3423
 */
3424
#define SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS"
3425
3426
/**
3427
 * A variable controlling whether the offscreen video driver saves output
3428
 * frames.
3429
 *
3430
 * This only saves frames that are generated using software rendering, not
3431
 * accelerated OpenGL rendering.
3432
 *
3433
 * - "0": Video frames are not saved to disk. (default)
3434
 * - "1": Video frames are saved to files in the format "SDL_windowX-Y.bmp",
3435
 *   where X is the window ID, and Y is the frame number.
3436
 *
3437
 * This hint can be set anytime.
3438
 *
3439
 * \since This hint is available since SDL 3.2.0.
3440
 */
3441
#define SDL_HINT_VIDEO_OFFSCREEN_SAVE_FRAMES "SDL_VIDEO_OFFSCREEN_SAVE_FRAMES"
3442
3443
/**
3444
 * A variable controlling whether all window operations will block until
3445
 * complete.
3446
 *
3447
 * Window systems that run asynchronously may not have the results of window
3448
 * operations that resize or move the window applied immediately upon the
3449
 * return of the requesting function. Setting this hint will cause such
3450
 * operations to block after every call until the pending operation has
3451
 * completed. Setting this to '1' is the equivalent of calling
3452
 * SDL_SyncWindow() after every function call.
3453
 *
3454
 * Be aware that amount of time spent blocking while waiting for window
3455
 * operations to complete can be quite lengthy, as animations may have to
3456
 * complete, which can take upwards of multiple seconds in some cases.
3457
 *
3458
 * The variable can be set to the following values:
3459
 *
3460
 * - "0": Window operations are non-blocking. (default)
3461
 * - "1": Window operations will block until completed.
3462
 *
3463
 * This hint can be set anytime.
3464
 *
3465
 * \since This hint is available since SDL 3.2.0.
3466
 */
3467
#define SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS "SDL_VIDEO_SYNC_WINDOW_OPERATIONS"
3468
3469
/**
3470
 * A variable controlling whether the libdecor Wayland backend is allowed to
3471
 * be used.
3472
 *
3473
 * libdecor is used over xdg-shell when xdg-decoration protocol is
3474
 * unavailable.
3475
 *
3476
 * The variable can be set to the following values:
3477
 *
3478
 * - "0": libdecor use is disabled.
3479
 * - "1": libdecor use is enabled. (default)
3480
 *
3481
 * This hint should be set before SDL is initialized.
3482
 *
3483
 * \since This hint is available since SDL 3.2.0.
3484
 */
3485
#define SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR "SDL_VIDEO_WAYLAND_ALLOW_LIBDECOR"
3486
3487
/**
3488
 * A variable controlling whether video mode emulation is enabled under
3489
 * Wayland.
3490
 *
3491
 * When this hint is set, a standard set of emulated CVT video modes will be
3492
 * exposed for use by the application. If it is disabled, the only modes
3493
 * exposed will be the logical desktop size and, in the case of a scaled
3494
 * desktop, the native display resolution.
3495
 *
3496
 * The variable can be set to the following values:
3497
 *
3498
 * - "0": Video mode emulation is disabled.
3499
 * - "1": Video mode emulation is enabled. (default)
3500
 *
3501
 * This hint should be set before SDL is initialized.
3502
 *
3503
 * \since This hint is available since SDL 3.2.0.
3504
 */
3505
#define SDL_HINT_VIDEO_WAYLAND_MODE_EMULATION "SDL_VIDEO_WAYLAND_MODE_EMULATION"
3506
3507
/**
3508
 * A variable controlling how modes with a non-native aspect ratio are
3509
 * displayed under Wayland.
3510
 *
3511
 * When this hint is set, the requested scaling will be used when displaying
3512
 * fullscreen video modes that don't match the display's native aspect ratio.
3513
 * This is contingent on compositor viewport support.
3514
 *
3515
 * The variable can be set to the following values:
3516
 *
3517
 * - "aspect" - Video modes will be displayed scaled, in their proper aspect
3518
 *   ratio, with black bars.
3519
 * - "stretch" - Video modes will be scaled to fill the entire display.
3520
 *   (default)
3521
 * - "none" - Video modes will be displayed as 1:1 with no scaling.
3522
 *
3523
 * This hint should be set before creating a window.
3524
 *
3525
 * \since This hint is available since SDL 3.2.0.
3526
 */
3527
#define SDL_HINT_VIDEO_WAYLAND_MODE_SCALING "SDL_VIDEO_WAYLAND_MODE_SCALING"
3528
3529
/**
3530
 * A variable controlling whether the libdecor Wayland backend is preferred
3531
 * over native decorations.
3532
 *
3533
 * When this hint is set, libdecor will be used to provide window decorations,
3534
 * even if xdg-decoration is available. (Note that, by default, libdecor will
3535
 * use xdg-decoration itself if available).
3536
 *
3537
 * The variable can be set to the following values:
3538
 *
3539
 * - "0": libdecor is enabled only if server-side decorations are unavailable.
3540
 *   (default)
3541
 * - "1": libdecor is always enabled if available.
3542
 *
3543
 * This hint should be set before SDL is initialized.
3544
 *
3545
 * \since This hint is available since SDL 3.2.0.
3546
 */
3547
#define SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR "SDL_VIDEO_WAYLAND_PREFER_LIBDECOR"
3548
3549
/**
3550
 * A variable forcing non-DPI-aware Wayland windows to output at 1:1 scaling.
3551
 *
3552
 * This must be set before initializing the video subsystem.
3553
 *
3554
 * When this hint is set, Wayland windows that are not flagged as being
3555
 * DPI-aware will be output with scaling designed to force 1:1 pixel mapping.
3556
 *
3557
 * This is intended to allow legacy applications to be displayed without
3558
 * desktop scaling being applied, and has issues with certain display
3559
 * configurations, as this forces the window to behave in a way that Wayland
3560
 * desktops were not designed to accommodate:
3561
 *
3562
 * - Rounding errors can result with odd window sizes and/or desktop scales,
3563
 *   which can cause the window contents to appear slightly blurry.
3564
 * - Positioning the window may be imprecise due to unit conversions and
3565
 *   rounding.
3566
 * - The window may be unusably small on scaled desktops.
3567
 * - The window may jump in size when moving between displays of different
3568
 *   scale factors.
3569
 * - Displays may appear to overlap when using a multi-monitor setup with
3570
 *   scaling enabled.
3571
 * - Possible loss of cursor precision due to the logical size of the window
3572
 *   being reduced.
3573
 *
3574
 * New applications should be designed with proper DPI awareness handling
3575
 * instead of enabling this.
3576
 *
3577
 * The variable can be set to the following values:
3578
 *
3579
 * - "0": Windows will be scaled normally.
3580
 * - "1": Windows will be forced to scale to achieve 1:1 output.
3581
 *
3582
 * This hint should be set before creating a window.
3583
 *
3584
 * \since This hint is available since SDL 3.2.0.
3585
 */
3586
#define SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY "SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY"
3587
3588
/**
3589
 * A variable specifying which shader compiler to preload when using the
3590
 * Chrome ANGLE binaries.
3591
 *
3592
 * SDL has EGL and OpenGL ES2 support on Windows via the ANGLE project. It can
3593
 * use two different sets of binaries, those compiled by the user from source
3594
 * or those provided by the Chrome browser. In the later case, these binaries
3595
 * require that SDL loads a DLL providing the shader compiler.
3596
 *
3597
 * The variable can be set to the following values:
3598
 *
3599
 * - "d3dcompiler_46.dll" - best for Vista or later. (default)
3600
 * - "d3dcompiler_43.dll" - for XP support.
3601
 * - "none" - do not load any library, useful if you compiled ANGLE from
3602
 *   source and included the compiler in your binaries.
3603
 *
3604
 * This hint should be set before SDL is initialized.
3605
 *
3606
 * \since This hint is available since SDL 3.2.0.
3607
 */
3608
#define SDL_HINT_VIDEO_WIN_D3DCOMPILER "SDL_VIDEO_WIN_D3DCOMPILER"
3609
3610
/**
3611
 * A variable controlling whether SDL should call XSelectInput() to enable
3612
 * input events on X11 windows wrapped by SDL windows.
3613
 *
3614
 * The variable can be set to the following values:
3615
 *
3616
 * - "0": Don't call XSelectInput(), assuming the native window code has done
3617
 *   it already.
3618
 * - "1": Call XSelectInput() to enable input events. (default)
3619
 *
3620
 * This hint should be set before creating a window.
3621
 *
3622
 * \since This hint is available since SDL 3.2.10.
3623
 */
3624
#define SDL_HINT_VIDEO_X11_EXTERNAL_WINDOW_INPUT "SDL_VIDEO_X11_EXTERNAL_WINDOW_INPUT"
3625
3626
/**
3627
 * A variable controlling whether the X11 _NET_WM_BYPASS_COMPOSITOR hint
3628
 * should be used.
3629
 *
3630
 * The variable can be set to the following values:
3631
 *
3632
 * - "0": Disable _NET_WM_BYPASS_COMPOSITOR.
3633
 * - "1": Enable _NET_WM_BYPASS_COMPOSITOR. (default)
3634
 *
3635
 * This hint should be set before creating a window.
3636
 *
3637
 * \since This hint is available since SDL 3.2.0.
3638
 */
3639
#define SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"
3640
3641
/**
3642
 * A variable controlling whether the X11 _NET_WM_PING protocol should be
3643
 * supported.
3644
 *
3645
 * By default SDL will use _NET_WM_PING, but for applications that know they
3646
 * will not always be able to respond to ping requests in a timely manner they
3647
 * can turn it off to avoid the window manager thinking the app is hung.
3648
 *
3649
 * The variable can be set to the following values:
3650
 *
3651
 * - "0": Disable _NET_WM_PING.
3652
 * - "1": Enable _NET_WM_PING. (default)
3653
 *
3654
 * This hint should be set before creating a window.
3655
 *
3656
 * \since This hint is available since SDL 3.2.0.
3657
 */
3658
#define SDL_HINT_VIDEO_X11_NET_WM_PING "SDL_VIDEO_X11_NET_WM_PING"
3659
3660
/**
3661
 * A variable controlling whether SDL uses DirectColor visuals.
3662
 *
3663
 * The variable can be set to the following values:
3664
 *
3665
 * - "0": Disable DirectColor visuals.
3666
 * - "1": Enable DirectColor visuals. (default)
3667
 *
3668
 * This hint should be set before initializing the video subsystem.
3669
 *
3670
 * \since This hint is available since SDL 3.2.0.
3671
 */
3672
#define SDL_HINT_VIDEO_X11_NODIRECTCOLOR "SDL_VIDEO_X11_NODIRECTCOLOR"
3673
3674
/**
3675
 * A variable forcing the content scaling factor for X11 displays.
3676
 *
3677
 * The variable can be set to a floating point value in the range 1.0-10.0f
3678
 *
3679
 * This hint should be set before SDL is initialized.
3680
 *
3681
 * \since This hint is available since SDL 3.2.0.
3682
 */
3683
#define SDL_HINT_VIDEO_X11_SCALING_FACTOR "SDL_VIDEO_X11_SCALING_FACTOR"
3684
3685
/**
3686
 * A variable forcing the visual ID used for X11 display modes.
3687
 *
3688
 * This hint should be set before initializing the video subsystem.
3689
 *
3690
 * \since This hint is available since SDL 3.2.0.
3691
 */
3692
#define SDL_HINT_VIDEO_X11_VISUALID "SDL_VIDEO_X11_VISUALID"
3693
3694
/**
3695
 * A variable forcing the visual ID chosen for new X11 windows.
3696
 *
3697
 * This hint should be set before creating a window.
3698
 *
3699
 * \since This hint is available since SDL 3.2.0.
3700
 */
3701
#define SDL_HINT_VIDEO_X11_WINDOW_VISUALID "SDL_VIDEO_X11_WINDOW_VISUALID"
3702
3703
/**
3704
 * A variable controlling whether the X11 XRandR extension should be used.
3705
 *
3706
 * The variable can be set to the following values:
3707
 *
3708
 * - "0": Disable XRandR.
3709
 * - "1": Enable XRandR. (default)
3710
 *
3711
 * This hint should be set before SDL is initialized.
3712
 *
3713
 * \since This hint is available since SDL 3.2.0.
3714
 */
3715
#define SDL_HINT_VIDEO_X11_XRANDR "SDL_VIDEO_X11_XRANDR"
3716
3717
/**
3718
 * A variable controlling whether touch should be enabled on the back panel of
3719
 * the PlayStation Vita.
3720
 *
3721
 * The variable can be set to the following values:
3722
 *
3723
 * - "0": Disable touch on the back panel.
3724
 * - "1": Enable touch on the back panel. (default)
3725
 *
3726
 * This hint should be set before SDL is initialized.
3727
 *
3728
 * \since This hint is available since SDL 3.2.0.
3729
 */
3730
#define SDL_HINT_VITA_ENABLE_BACK_TOUCH "SDL_VITA_ENABLE_BACK_TOUCH"
3731
3732
/**
3733
 * A variable controlling whether touch should be enabled on the front panel
3734
 * of the PlayStation Vita.
3735
 *
3736
 * The variable can be set to the following values:
3737
 *
3738
 * - "0": Disable touch on the front panel.
3739
 * - "1": Enable touch on the front panel. (default)
3740
 *
3741
 * This hint should be set before SDL is initialized.
3742
 *
3743
 * \since This hint is available since SDL 3.2.0.
3744
 */
3745
#define SDL_HINT_VITA_ENABLE_FRONT_TOUCH "SDL_VITA_ENABLE_FRONT_TOUCH"
3746
3747
/**
3748
 * A variable controlling the module path on the PlayStation Vita.
3749
 *
3750
 * This hint defaults to "app0:module"
3751
 *
3752
 * This hint should be set before SDL is initialized.
3753
 *
3754
 * \since This hint is available since SDL 3.2.0.
3755
 */
3756
#define SDL_HINT_VITA_MODULE_PATH "SDL_VITA_MODULE_PATH"
3757
3758
/**
3759
 * A variable controlling whether to perform PVR initialization on the
3760
 * PlayStation Vita.
3761
 *
3762
 * - "0": Skip PVR initialization.
3763
 * - "1": Perform the normal PVR initialization. (default)
3764
 *
3765
 * This hint should be set before SDL is initialized.
3766
 *
3767
 * \since This hint is available since SDL 3.2.0.
3768
 */
3769
#define SDL_HINT_VITA_PVR_INIT "SDL_VITA_PVR_INIT"
3770
3771
/**
3772
 * A variable overriding the resolution reported on the PlayStation Vita.
3773
 *
3774
 * The variable can be set to the following values:
3775
 *
3776
 * - "544": 544p (default)
3777
 * - "720": 725p for PSTV
3778
 * - "1080": 1088i for PSTV
3779
 *
3780
 * This hint should be set before SDL is initialized.
3781
 *
3782
 * \since This hint is available since SDL 3.2.0.
3783
 */
3784
#define SDL_HINT_VITA_RESOLUTION "SDL_VITA_RESOLUTION"
3785
3786
/**
3787
 * A variable controlling whether OpenGL should be used instead of OpenGL ES
3788
 * on the PlayStation Vita.
3789
 *
3790
 * The variable can be set to the following values:
3791
 *
3792
 * - "0": Use OpenGL ES. (default)
3793
 * - "1": Use OpenGL.
3794
 *
3795
 * This hint should be set before SDL is initialized.
3796
 *
3797
 * \since This hint is available since SDL 3.2.0.
3798
 */
3799
#define SDL_HINT_VITA_PVR_OPENGL "SDL_VITA_PVR_OPENGL"
3800
3801
/**
3802
 * A variable controlling which touchpad should generate synthetic mouse
3803
 * events.
3804
 *
3805
 * The variable can be set to the following values:
3806
 *
3807
 * - "0": Only front touchpad should generate mouse events. (default)
3808
 * - "1": Only back touchpad should generate mouse events.
3809
 * - "2": Both touchpads should generate mouse events.
3810
 *
3811
 * This hint can be set anytime.
3812
 *
3813
 * \since This hint is available since SDL 3.2.0.
3814
 */
3815
#define SDL_HINT_VITA_TOUCH_MOUSE_DEVICE "SDL_VITA_TOUCH_MOUSE_DEVICE"
3816
3817
/**
3818
 * A variable overriding the display index used in SDL_Vulkan_CreateSurface()
3819
 *
3820
 * The display index starts at 0, which is the default.
3821
 *
3822
 * This hint should be set before calling SDL_Vulkan_CreateSurface()
3823
 *
3824
 * \since This hint is available since SDL 3.2.0.
3825
 */
3826
#define SDL_HINT_VULKAN_DISPLAY "SDL_VULKAN_DISPLAY"
3827
3828
/**
3829
 * Specify the Vulkan library to load.
3830
 *
3831
 * This hint should be set before creating a Vulkan window or calling
3832
 * SDL_Vulkan_LoadLibrary().
3833
 *
3834
 * \since This hint is available since SDL 3.2.0.
3835
 */
3836
#define SDL_HINT_VULKAN_LIBRARY "SDL_VULKAN_LIBRARY"
3837
3838
/**
3839
 * A variable controlling how the fact chunk affects the loading of a WAVE
3840
 * file.
3841
 *
3842
 * The fact chunk stores information about the number of samples of a WAVE
3843
 * file. The Standards Update from Microsoft notes that this value can be used
3844
 * to 'determine the length of the data in seconds'. This is especially useful
3845
 * for compressed formats (for which this is a mandatory chunk) if they
3846
 * produce multiple sample frames per block and truncating the block is not
3847
 * allowed. The fact chunk can exactly specify how many sample frames there
3848
 * should be in this case.
3849
 *
3850
 * Unfortunately, most application seem to ignore the fact chunk and so SDL
3851
 * ignores it by default as well.
3852
 *
3853
 * The variable can be set to the following values:
3854
 *
3855
 * - "truncate" - Use the number of samples to truncate the wave data if the
3856
 *   fact chunk is present and valid.
3857
 * - "strict" - Like "truncate", but raise an error if the fact chunk is
3858
 *   invalid, not present for non-PCM formats, or if the data chunk doesn't
3859
 *   have that many samples.
3860
 * - "ignorezero" - Like "truncate", but ignore fact chunk if the number of
3861
 *   samples is zero.
3862
 * - "ignore" - Ignore fact chunk entirely. (default)
3863
 *
3864
 * This hint should be set before calling SDL_LoadWAV() or SDL_LoadWAV_IO()
3865
 *
3866
 * \since This hint is available since SDL 3.2.0.
3867
 */
3868
#define SDL_HINT_WAVE_FACT_CHUNK "SDL_WAVE_FACT_CHUNK"
3869
3870
/**
3871
 * A variable controlling the maximum number of chunks in a WAVE file.
3872
 *
3873
 * This sets an upper bound on the number of chunks in a WAVE file to avoid
3874
 * wasting time on malformed or corrupt WAVE files. This defaults to "10000".
3875
 *
3876
 * This hint should be set before calling SDL_LoadWAV() or SDL_LoadWAV_IO()
3877
 *
3878
 * \since This hint is available since SDL 3.2.0.
3879
 */
3880
#define SDL_HINT_WAVE_CHUNK_LIMIT "SDL_WAVE_CHUNK_LIMIT"
3881
3882
/**
3883
 * A variable controlling how the size of the RIFF chunk affects the loading
3884
 * of a WAVE file.
3885
 *
3886
 * The size of the RIFF chunk (which includes all the sub-chunks of the WAVE
3887
 * file) is not always reliable. In case the size is wrong, it's possible to
3888
 * just ignore it and step through the chunks until a fixed limit is reached.
3889
 *
3890
 * Note that files that have trailing data unrelated to the WAVE file or
3891
 * corrupt files may slow down the loading process without a reliable
3892
 * boundary. By default, SDL stops after 10000 chunks to prevent wasting time.
3893
 * Use SDL_HINT_WAVE_CHUNK_LIMIT to adjust this value.
3894
 *
3895
 * The variable can be set to the following values:
3896
 *
3897
 * - "force" - Always use the RIFF chunk size as a boundary for the chunk
3898
 *   search.
3899
 * - "ignorezero" - Like "force", but a zero size searches up to 4 GiB.
3900
 *   (default)
3901
 * - "ignore" - Ignore the RIFF chunk size and always search up to 4 GiB.
3902
 * - "maximum" - Search for chunks until the end of file. (not recommended)
3903
 *
3904
 * This hint should be set before calling SDL_LoadWAV() or SDL_LoadWAV_IO()
3905
 *
3906
 * \since This hint is available since SDL 3.2.0.
3907
 */
3908
#define SDL_HINT_WAVE_RIFF_CHUNK_SIZE "SDL_WAVE_RIFF_CHUNK_SIZE"
3909
3910
/**
3911
 * A variable controlling how a truncated WAVE file is handled.
3912
 *
3913
 * A WAVE file is considered truncated if any of the chunks are incomplete or
3914
 * the data chunk size is not a multiple of the block size. By default, SDL
3915
 * decodes until the first incomplete block, as most applications seem to do.
3916
 *
3917
 * The variable can be set to the following values:
3918
 *
3919
 * - "verystrict" - Raise an error if the file is truncated.
3920
 * - "strict" - Like "verystrict", but the size of the RIFF chunk is ignored.
3921
 * - "dropframe" - Decode until the first incomplete sample frame.
3922
 * - "dropblock" - Decode until the first incomplete block. (default)
3923
 *
3924
 * This hint should be set before calling SDL_LoadWAV() or SDL_LoadWAV_IO()
3925
 *
3926
 * \since This hint is available since SDL 3.2.0.
3927
 */
3928
#define SDL_HINT_WAVE_TRUNCATION "SDL_WAVE_TRUNCATION"
3929
3930
/**
3931
 * A variable controlling whether the window is activated when the
3932
 * SDL_RaiseWindow function is called.
3933
 *
3934
 * The variable can be set to the following values:
3935
 *
3936
 * - "0": The window is not activated when the SDL_RaiseWindow function is
3937
 *   called.
3938
 * - "1": The window is activated when the SDL_RaiseWindow function is called.
3939
 *   (default)
3940
 *
3941
 * This hint can be set anytime.
3942
 *
3943
 * \since This hint is available since SDL 3.2.0.
3944
 */
3945
#define SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED "SDL_WINDOW_ACTIVATE_WHEN_RAISED"
3946
3947
/**
3948
 * A variable controlling whether the window is activated when the
3949
 * SDL_ShowWindow function is called.
3950
 *
3951
 * The variable can be set to the following values:
3952
 *
3953
 * - "0": The window is not activated when the SDL_ShowWindow function is
3954
 *   called.
3955
 * - "1": The window is activated when the SDL_ShowWindow function is called.
3956
 *   (default)
3957
 *
3958
 * This hint can be set anytime.
3959
 *
3960
 * \since This hint is available since SDL 3.2.0.
3961
 */
3962
#define SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN "SDL_WINDOW_ACTIVATE_WHEN_SHOWN"
3963
3964
/**
3965
 * If set to "0" then never set the top-most flag on an SDL Window even if the
3966
 * application requests it.
3967
 *
3968
 * This is a debugging aid for developers and not expected to be used by end
3969
 * users.
3970
 *
3971
 * The variable can be set to the following values:
3972
 *
3973
 * - "0": don't allow topmost
3974
 * - "1": allow topmost (default)
3975
 *
3976
 * This hint can be set anytime.
3977
 *
3978
 * \since This hint is available since SDL 3.2.0.
3979
 */
3980
#define SDL_HINT_WINDOW_ALLOW_TOPMOST "SDL_WINDOW_ALLOW_TOPMOST"
3981
3982
/**
3983
 * A variable controlling whether the window frame and title bar are
3984
 * interactive when the cursor is hidden.
3985
 *
3986
 * The variable can be set to the following values:
3987
 *
3988
 * - "0": The window frame is not interactive when the cursor is hidden (no
3989
 *   move, resize, etc).
3990
 * - "1": The window frame is interactive when the cursor is hidden. (default)
3991
 *
3992
 * This hint can be set anytime.
3993
 *
3994
 * \since This hint is available since SDL 3.2.0.
3995
 */
3996
#define SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN "SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN"
3997
3998
/**
3999
 * A variable controlling whether SDL generates window-close events for Alt+F4
4000
 * on Windows.
4001
 *
4002
 * The variable can be set to the following values:
4003
 *
4004
 * - "0": SDL will only do normal key handling for Alt+F4.
4005
 * - "1": SDL will generate a window-close event when it sees Alt+F4.
4006
 *   (default)
4007
 *
4008
 * This hint can be set anytime.
4009
 *
4010
 * \since This hint is available since SDL 3.2.0.
4011
 */
4012
#define SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4 "SDL_WINDOWS_CLOSE_ON_ALT_F4"
4013
4014
/**
4015
 * A variable controlling whether menus can be opened with their keyboard
4016
 * shortcut (Alt+mnemonic).
4017
 *
4018
 * If the mnemonics are enabled, then menus can be opened by pressing the Alt
4019
 * key and the corresponding mnemonic (for example, Alt+F opens the File
4020
 * menu). However, in case an invalid mnemonic is pressed, Windows makes an
4021
 * audible beep to convey that nothing happened. This is true even if the
4022
 * window has no menu at all!
4023
 *
4024
 * Because most SDL applications don't have menus, and some want to use the
4025
 * Alt key for other purposes, SDL disables mnemonics (and the beeping) by
4026
 * default.
4027
 *
4028
 * Note: This also affects keyboard events: with mnemonics enabled, when a
4029
 * menu is opened from the keyboard, you will not receive a KEYUP event for
4030
 * the mnemonic key, and *might* not receive one for Alt.
4031
 *
4032
 * The variable can be set to the following values:
4033
 *
4034
 * - "0": Alt+mnemonic does nothing, no beeping. (default)
4035
 * - "1": Alt+mnemonic opens menus, invalid mnemonics produce a beep.
4036
 *
4037
 * This hint can be set anytime.
4038
 *
4039
 * \since This hint is available since SDL 3.2.0.
4040
 */
4041
#define SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS "SDL_WINDOWS_ENABLE_MENU_MNEMONICS"
4042
4043
/**
4044
 * A variable controlling whether the windows message loop is processed by
4045
 * SDL.
4046
 *
4047
 * The variable can be set to the following values:
4048
 *
4049
 * - "0": The window message loop is not run.
4050
 * - "1": The window message loop is processed in SDL_PumpEvents(). (default)
4051
 *
4052
 * This hint can be set anytime.
4053
 *
4054
 * \since This hint is available since SDL 3.2.0.
4055
 */
4056
#define SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP "SDL_WINDOWS_ENABLE_MESSAGELOOP"
4057
4058
/**
4059
 * A variable controlling whether GameInput is used for raw keyboard and mouse
4060
 * on Windows.
4061
 *
4062
 * The variable can be set to the following values:
4063
 *
4064
 * - "0": GameInput is not used for raw keyboard and mouse events.
4065
 * - "1": GameInput is used for raw keyboard and mouse events, if available.
4066
 *   (default)
4067
 *
4068
 * This hint should be set before SDL is initialized.
4069
 *
4070
 * \since This hint is available since SDL 3.2.0.
4071
 */
4072
#define SDL_HINT_WINDOWS_GAMEINPUT   "SDL_WINDOWS_GAMEINPUT"
4073
4074
/**
4075
 * A variable controlling whether raw keyboard events are used on Windows.
4076
 *
4077
 * The variable can be set to the following values:
4078
 *
4079
 * - "0": The Windows message loop is used for keyboard events. (default)
4080
 * - "1": Low latency raw keyboard events are used.
4081
 *
4082
 * This hint can be set anytime.
4083
 *
4084
 * \since This hint is available since SDL 3.2.0.
4085
 */
4086
#define SDL_HINT_WINDOWS_RAW_KEYBOARD "SDL_WINDOWS_RAW_KEYBOARD"
4087
4088
/**
4089
 * A variable controlling whether SDL uses Kernel Semaphores on Windows.
4090
 *
4091
 * Kernel Semaphores are inter-process and require a context switch on every
4092
 * interaction. On Windows 8 and newer, the WaitOnAddress API is available.
4093
 * Using that and atomics to implement semaphores increases performance. SDL
4094
 * will fall back to Kernel Objects on older OS versions or if forced to by
4095
 * this hint.
4096
 *
4097
 * The variable can be set to the following values:
4098
 *
4099
 * - "0": Use Atomics and WaitOnAddress API when available, otherwise fall
4100
 *   back to Kernel Objects. (default)
4101
 * - "1": Force the use of Kernel Objects in all cases.
4102
 *
4103
 * This hint should be set before SDL is initialized.
4104
 *
4105
 * \since This hint is available since SDL 3.2.0.
4106
 */
4107
#define SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL"
4108
4109
/**
4110
 * A variable to specify custom icon resource id from RC file on Windows
4111
 * platform.
4112
 *
4113
 * This hint should be set before SDL is initialized.
4114
 *
4115
 * \since This hint is available since SDL 3.2.0.
4116
 */
4117
#define SDL_HINT_WINDOWS_INTRESOURCE_ICON       "SDL_WINDOWS_INTRESOURCE_ICON"
4118
4119
/**
4120
 * A variable to specify custom icon resource id from RC file on Windows
4121
 * platform.
4122
 *
4123
 * This hint should be set before SDL is initialized.
4124
 *
4125
 * \since This hint is available since SDL 3.2.0.
4126
 */
4127
#define SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL "SDL_WINDOWS_INTRESOURCE_ICON_SMALL"
4128
4129
/**
4130
 * A variable controlling whether SDL uses the D3D9Ex API introduced in
4131
 * Windows Vista, instead of normal D3D9.
4132
 *
4133
 * Direct3D 9Ex contains changes to state management that can eliminate device
4134
 * loss errors during scenarios like Alt+Tab or UAC prompts. D3D9Ex may
4135
 * require some changes to your application to cope with the new behavior, so
4136
 * this is disabled by default.
4137
 *
4138
 * For more information on Direct3D 9Ex, see:
4139
 *
4140
 * - https://docs.microsoft.com/en-us/windows/win32/direct3darticles/graphics-apis-in-windows-vista#direct3d-9ex
4141
 * - https://docs.microsoft.com/en-us/windows/win32/direct3darticles/direct3d-9ex-improvements
4142
 *
4143
 * The variable can be set to the following values:
4144
 *
4145
 * - "0": Use the original Direct3D 9 API. (default)
4146
 * - "1": Use the Direct3D 9Ex API on Vista and later (and fall back if D3D9Ex
4147
 *   is unavailable)
4148
 *
4149
 * This hint should be set before SDL is initialized.
4150
 *
4151
 * \since This hint is available since SDL 3.2.0.
4152
 */
4153
#define SDL_HINT_WINDOWS_USE_D3D9EX "SDL_WINDOWS_USE_D3D9EX"
4154
4155
/**
4156
 * A variable controlling whether SDL will clear the window contents when the
4157
 * WM_ERASEBKGND message is received.
4158
 *
4159
 * The variable can be set to the following values:
4160
 *
4161
 * - "0"/"never": Never clear the window.
4162
 * - "1"/"initial": Clear the window when the first WM_ERASEBKGND event fires.
4163
 *   (default)
4164
 * - "2"/"always": Clear the window on every WM_ERASEBKGND event.
4165
 *
4166
 * This hint should be set before creating a window.
4167
 *
4168
 * \since This hint is available since SDL 3.2.0.
4169
 */
4170
#define SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE "SDL_WINDOWS_ERASE_BACKGROUND_MODE"
4171
4172
/**
4173
 * A variable controlling whether X11 windows are marked as override-redirect.
4174
 *
4175
 * If set, this _might_ increase framerate at the expense of the desktop not
4176
 * working as expected. Override-redirect windows aren't noticed by the window
4177
 * manager at all.
4178
 *
4179
 * You should probably only use this for fullscreen windows, and you probably
4180
 * shouldn't even use it for that. But it's here if you want to try!
4181
 *
4182
 * The variable can be set to the following values:
4183
 *
4184
 * - "0": Do not mark the window as override-redirect. (default)
4185
 * - "1": Mark the window as override-redirect.
4186
 *
4187
 * This hint should be set before creating a window.
4188
 *
4189
 * \since This hint is available since SDL 3.2.0.
4190
 */
4191
#define SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT "SDL_X11_FORCE_OVERRIDE_REDIRECT"
4192
4193
/**
4194
 * A variable specifying the type of an X11 window.
4195
 *
4196
 * During SDL_CreateWindow, SDL uses the _NET_WM_WINDOW_TYPE X11 property to
4197
 * report to the window manager the type of window it wants to create. This
4198
 * might be set to various things if SDL_WINDOW_TOOLTIP or
4199
 * SDL_WINDOW_POPUP_MENU, etc, were specified. For "normal" windows that
4200
 * haven't set a specific type, this hint can be used to specify a custom
4201
 * type. For example, a dock window might set this to
4202
 * "_NET_WM_WINDOW_TYPE_DOCK".
4203
 *
4204
 * This hint should be set before creating a window.
4205
 *
4206
 * \since This hint is available since SDL 3.2.0.
4207
 */
4208
#define SDL_HINT_X11_WINDOW_TYPE "SDL_X11_WINDOW_TYPE"
4209
4210
/**
4211
 * Specify the XCB library to load for the X11 driver.
4212
 *
4213
 * The default is platform-specific, often "libX11-xcb.so.1".
4214
 *
4215
 * This hint should be set before initializing the video subsystem.
4216
 *
4217
 * \since This hint is available since SDL 3.2.0.
4218
 */
4219
#define SDL_HINT_X11_XCB_LIBRARY "SDL_X11_XCB_LIBRARY"
4220
4221
/**
4222
 * A variable controlling whether XInput should be used for controller
4223
 * handling.
4224
 *
4225
 * The variable can be set to the following values:
4226
 *
4227
 * - "0": XInput is not enabled.
4228
 * - "1": XInput is enabled. (default)
4229
 *
4230
 * This hint should be set before SDL is initialized.
4231
 *
4232
 * \since This hint is available since SDL 3.2.0.
4233
 */
4234
#define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED"
4235
4236
/**
4237
 * A variable controlling response to SDL_assert failures.
4238
 *
4239
 * The variable can be set to the following case-sensitive values:
4240
 *
4241
 * - "abort": Program terminates immediately.
4242
 * - "break": Program triggers a debugger breakpoint.
4243
 * - "retry": Program reruns the SDL_assert's test again.
4244
 * - "ignore": Program continues on, ignoring this assertion failure this
4245
 *   time.
4246
 * - "always_ignore": Program continues on, ignoring this assertion failure
4247
 *   for the rest of the run.
4248
 *
4249
 * Note that SDL_SetAssertionHandler offers a programmatic means to deal with
4250
 * assertion failures through a callback, and this hint is largely intended to
4251
 * be used via environment variables by end users and automated tools.
4252
 *
4253
 * This hint should be set before an assertion failure is triggered and can be
4254
 * changed at any time.
4255
 *
4256
 * \since This hint is available since SDL 3.2.0.
4257
 */
4258
#define SDL_HINT_ASSERT "SDL_ASSERT"
4259
4260
/**
4261
 * A variable controlling whether pen events should generate synthetic mouse
4262
 * events.
4263
 *
4264
 * The variable can be set to the following values:
4265
 *
4266
 * - "0": Pen events will not generate mouse events.
4267
 * - "1": Pen events will generate mouse events. (default)
4268
 *
4269
 * This hint can be set anytime.
4270
 *
4271
 * \since This hint is available since SDL 3.2.0.
4272
 */
4273
#define SDL_HINT_PEN_MOUSE_EVENTS "SDL_PEN_MOUSE_EVENTS"
4274
4275
/**
4276
 * A variable controlling whether pen events should generate synthetic touch
4277
 * events.
4278
 *
4279
 * The variable can be set to the following values:
4280
 *
4281
 * - "0": Pen events will not generate touch events.
4282
 * - "1": Pen events will generate touch events. (default)
4283
 *
4284
 * This hint can be set anytime.
4285
 *
4286
 * \since This hint is available since SDL 3.2.0.
4287
 */
4288
#define SDL_HINT_PEN_TOUCH_EVENTS "SDL_PEN_TOUCH_EVENTS"
4289
4290
4291
/**
4292
 * An enumeration of hint priorities.
4293
 *
4294
 * \since This enum is available since SDL 3.2.0.
4295
 */
4296
typedef enum SDL_HintPriority
4297
{
4298
    SDL_HINT_DEFAULT,
4299
    SDL_HINT_NORMAL,
4300
    SDL_HINT_OVERRIDE
4301
} SDL_HintPriority;
4302
4303
/**
4304
 * Set a hint with a specific priority.
4305
 *
4306
 * The priority controls the behavior when setting a hint that already has a
4307
 * value. Hints will replace existing hints of their priority and lower.
4308
 * Environment variables are considered to have override priority.
4309
 *
4310
 * \param name the hint to set.
4311
 * \param value the value of the hint variable.
4312
 * \param priority the SDL_HintPriority level for the hint.
4313
 * \returns true on success or false on failure; call SDL_GetError() for more
4314
 *          information.
4315
 *
4316
 * \threadsafety It is safe to call this function from any thread.
4317
 *
4318
 * \since This function is available since SDL 3.2.0.
4319
 *
4320
 * \sa SDL_GetHint
4321
 * \sa SDL_ResetHint
4322
 * \sa SDL_SetHint
4323
 */
4324
extern SDL_DECLSPEC bool SDLCALL SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority);
4325
4326
/**
4327
 * Set a hint with normal priority.
4328
 *
4329
 * Hints will not be set if there is an existing override hint or environment
4330
 * variable that takes precedence. You can use SDL_SetHintWithPriority() to
4331
 * set the hint with override priority instead.
4332
 *
4333
 * \param name the hint to set.
4334
 * \param value the value of the hint variable.
4335
 * \returns true on success or false on failure; call SDL_GetError() for more
4336
 *          information.
4337
 *
4338
 * \threadsafety It is safe to call this function from any thread.
4339
 *
4340
 * \since This function is available since SDL 3.2.0.
4341
 *
4342
 * \sa SDL_GetHint
4343
 * \sa SDL_ResetHint
4344
 * \sa SDL_SetHintWithPriority
4345
 */
4346
extern SDL_DECLSPEC bool SDLCALL SDL_SetHint(const char *name, const char *value);
4347
4348
/**
4349
 * Reset a hint to the default value.
4350
 *
4351
 * This will reset a hint to the value of the environment variable, or NULL if
4352
 * the environment isn't set. Callbacks will be called normally with this
4353
 * change.
4354
 *
4355
 * \param name the hint to set.
4356
 * \returns true on success or false on failure; call SDL_GetError() for more
4357
 *          information.
4358
 *
4359
 * \threadsafety It is safe to call this function from any thread.
4360
 *
4361
 * \since This function is available since SDL 3.2.0.
4362
 *
4363
 * \sa SDL_SetHint
4364
 * \sa SDL_ResetHints
4365
 */
4366
extern SDL_DECLSPEC bool SDLCALL SDL_ResetHint(const char *name);
4367
4368
/**
4369
 * Reset all hints to the default values.
4370
 *
4371
 * This will reset all hints to the value of the associated environment
4372
 * variable, or NULL if the environment isn't set. Callbacks will be called
4373
 * normally with this change.
4374
 *
4375
 * \threadsafety It is safe to call this function from any thread.
4376
 *
4377
 * \since This function is available since SDL 3.2.0.
4378
 *
4379
 * \sa SDL_ResetHint
4380
 */
4381
extern SDL_DECLSPEC void SDLCALL SDL_ResetHints(void);
4382
4383
/**
4384
 * Get the value of a hint.
4385
 *
4386
 * \param name the hint to query.
4387
 * \returns the string value of a hint or NULL if the hint isn't set.
4388
 *
4389
 * \threadsafety It is safe to call this function from any thread, however the
4390
 *               return value only remains valid until the hint is changed; if
4391
 *               another thread might do so, the app should supply locks
4392
 *               and/or make a copy of the string. Note that using a hint
4393
 *               callback instead is always thread-safe, as SDL holds a lock
4394
 *               on the thread subsystem during the callback.
4395
 *
4396
 * \since This function is available since SDL 3.2.0.
4397
 *
4398
 * \sa SDL_SetHint
4399
 * \sa SDL_SetHintWithPriority
4400
 */
4401
extern SDL_DECLSPEC const char * SDLCALL SDL_GetHint(const char *name);
4402
4403
/**
4404
 * Get the boolean value of a hint variable.
4405
 *
4406
 * \param name the name of the hint to get the boolean value from.
4407
 * \param default_value the value to return if the hint does not exist.
4408
 * \returns the boolean value of a hint or the provided default value if the
4409
 *          hint does not exist.
4410
 *
4411
 * \threadsafety It is safe to call this function from any thread.
4412
 *
4413
 * \since This function is available since SDL 3.2.0.
4414
 *
4415
 * \sa SDL_GetHint
4416
 * \sa SDL_SetHint
4417
 */
4418
extern SDL_DECLSPEC bool SDLCALL SDL_GetHintBoolean(const char *name, bool default_value);
4419
4420
/**
4421
 * A callback used to send notifications of hint value changes.
4422
 *
4423
 * This is called an initial time during SDL_AddHintCallback with the hint's
4424
 * current value, and then again each time the hint's value changes.
4425
 *
4426
 * \param userdata what was passed as `userdata` to SDL_AddHintCallback().
4427
 * \param name what was passed as `name` to SDL_AddHintCallback().
4428
 * \param oldValue the previous hint value.
4429
 * \param newValue the new value hint is to be set to.
4430
 *
4431
 * \threadsafety This callback is fired from whatever thread is setting a new
4432
 *               hint value. SDL holds a lock on the hint subsystem when
4433
 *               calling this callback.
4434
 *
4435
 * \since This datatype is available since SDL 3.2.0.
4436
 *
4437
 * \sa SDL_AddHintCallback
4438
 */
4439
typedef void(SDLCALL *SDL_HintCallback)(void *userdata, const char *name, const char *oldValue, const char *newValue);
4440
4441
/**
4442
 * Add a function to watch a particular hint.
4443
 *
4444
 * The callback function is called _during_ this function, to provide it an
4445
 * initial value, and again each time the hint's value changes.
4446
 *
4447
 * \param name the hint to watch.
4448
 * \param callback An SDL_HintCallback function that will be called when the
4449
 *                 hint value changes.
4450
 * \param userdata a pointer to pass to the callback function.
4451
 * \returns true on success or false on failure; call SDL_GetError() for more
4452
 *          information.
4453
 *
4454
 * \threadsafety It is safe to call this function from any thread.
4455
 *
4456
 * \since This function is available since SDL 3.2.0.
4457
 *
4458
 * \sa SDL_RemoveHintCallback
4459
 */
4460
extern SDL_DECLSPEC bool SDLCALL SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata);
4461
4462
/**
4463
 * Remove a function watching a particular hint.
4464
 *
4465
 * \param name the hint being watched.
4466
 * \param callback an SDL_HintCallback function that will be called when the
4467
 *                 hint value changes.
4468
 * \param userdata a pointer being passed to the callback function.
4469
 *
4470
 * \threadsafety It is safe to call this function from any thread.
4471
 *
4472
 * \since This function is available since SDL 3.2.0.
4473
 *
4474
 * \sa SDL_AddHintCallback
4475
 */
4476
extern SDL_DECLSPEC void SDLCALL SDL_RemoveHintCallback(const char *name,
4477
                                                     SDL_HintCallback callback,
4478
                                                     void *userdata);
4479
4480
/* Ends C function definitions when using C++ */
4481
#ifdef __cplusplus
4482
}
4483
#endif
4484
#include <SDL3/SDL_close_code.h>
4485
4486
#endif /* SDL_hints_h_ */
/opt/homebrew/include/SDL3/SDL_init.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryInit
24
 *
25
 * All SDL programs need to initialize the library before starting to work
26
 * with it.
27
 *
28
 * Almost everything can simply call SDL_Init() near startup, with a handful
29
 * of flags to specify subsystems to touch. These are here to make sure SDL
30
 * does not even attempt to touch low-level pieces of the operating system
31
 * that you don't intend to use. For example, you might be using SDL for video
32
 * and input but chose an external library for audio, and in this case you
33
 * would just need to leave off the `SDL_INIT_AUDIO` flag to make sure that
34
 * external library has complete control.
35
 *
36
 * Most apps, when terminating, should call SDL_Quit(). This will clean up
37
 * (nearly) everything that SDL might have allocated, and crucially, it'll
38
 * make sure that the display's resolution is back to what the user expects if
39
 * you had previously changed it for your game.
40
 *
41
 * SDL3 apps are strongly encouraged to call SDL_SetAppMetadata() at startup
42
 * to fill in details about the program. This is completely optional, but it
43
 * helps in small ways (we can provide an About dialog box for the macOS menu,
44
 * we can name the app in the system's audio mixer, etc). Those that want to
45
 * provide a _lot_ of information should look at the more-detailed
46
 * SDL_SetAppMetadataProperty().
47
 */
48
49
#ifndef SDL_init_h_
50
#define SDL_init_h_
51
52
#include <SDL3/SDL_stdinc.h>
53
#include <SDL3/SDL_error.h>
54
#include <SDL3/SDL_events.h>
55
56
#include <SDL3/SDL_begin_code.h>
57
/* Set up for C function definitions, even when using C++ */
58
#ifdef __cplusplus
59
extern "C" {
60
#endif
61
62
/* As of version 0.5, SDL is loaded dynamically into the application */
63
64
/**
65
 * Initialization flags for SDL_Init and/or SDL_InitSubSystem
66
 *
67
 * These are the flags which may be passed to SDL_Init(). You should specify
68
 * the subsystems which you will be using in your application.
69
 *
70
 * \since This datatype is available since SDL 3.2.0.
71
 *
72
 * \sa SDL_Init
73
 * \sa SDL_Quit
74
 * \sa SDL_InitSubSystem
75
 * \sa SDL_QuitSubSystem
76
 * \sa SDL_WasInit
77
 */
78
typedef Uint32 SDL_InitFlags;
79
80
7
#define SDL_INIT_AUDIO      0x00000010u /**< `SDL_INIT_AUDIO` implies `SDL_INIT_EVENTS` */
81
1
#define SDL_INIT_VIDEO      0x00000020u /**< `SDL_INIT_VIDEO` implies `SDL_INIT_EVENTS`, should be initialized on the main thread */
82
#define SDL_INIT_JOYSTICK   0x00000200u /**< `SDL_INIT_JOYSTICK` implies `SDL_INIT_EVENTS` */
83
#define SDL_INIT_HAPTIC     0x00001000u
84
#define SDL_INIT_GAMEPAD    0x00002000u /**< `SDL_INIT_GAMEPAD` implies `SDL_INIT_JOYSTICK` */
85
#define SDL_INIT_EVENTS     0x00004000u
86
#define SDL_INIT_SENSOR     0x00008000u /**< `SDL_INIT_SENSOR` implies `SDL_INIT_EVENTS` */
87
#define SDL_INIT_CAMERA     0x00010000u /**< `SDL_INIT_CAMERA` implies `SDL_INIT_EVENTS` */
88
89
/**
90
 * Return values for optional main callbacks.
91
 *
92
 * Returning SDL_APP_SUCCESS or SDL_APP_FAILURE from SDL_AppInit,
93
 * SDL_AppEvent, or SDL_AppIterate will terminate the program and report
94
 * success/failure to the operating system. What that means is
95
 * platform-dependent. On Unix, for example, on success, the process error
96
 * code will be zero, and on failure it will be 1. This interface doesn't
97
 * allow you to return specific exit codes, just whether there was an error
98
 * generally or not.
99
 *
100
 * Returning SDL_APP_CONTINUE from these functions will let the app continue
101
 * to run.
102
 *
103
 * See
104
 * [Main callbacks in SDL3](https://wiki.libsdl.org/SDL3/README/main-functions#main-callbacks-in-sdl3)
105
 * for complete details.
106
 *
107
 * \since This enum is available since SDL 3.2.0.
108
 */
109
typedef enum SDL_AppResult
110
{
111
    SDL_APP_CONTINUE,   /**< Value that requests that the app continue from the main callbacks. */
112
    SDL_APP_SUCCESS,    /**< Value that requests termination with success from the main callbacks. */
113
    SDL_APP_FAILURE     /**< Value that requests termination with error from the main callbacks. */
114
} SDL_AppResult;
115
116
/**
117
 * Function pointer typedef for SDL_AppInit.
118
 *
119
 * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind
120
 * the scenes for apps using the optional main callbacks. Apps that want to
121
 * use this should just implement SDL_AppInit directly.
122
 *
123
 * \param appstate a place where the app can optionally store a pointer for
124
 *                 future use.
125
 * \param argc the standard ANSI C main's argc; number of elements in `argv`.
126
 * \param argv the standard ANSI C main's argv; array of command line
127
 *             arguments.
128
 * \returns SDL_APP_FAILURE to terminate with an error, SDL_APP_SUCCESS to
129
 *          terminate with success, SDL_APP_CONTINUE to continue.
130
 *
131
 * \since This datatype is available since SDL 3.2.0.
132
 */
133
typedef SDL_AppResult (SDLCALL *SDL_AppInit_func)(void **appstate, int argc, char *argv[]);
134
135
/**
136
 * Function pointer typedef for SDL_AppIterate.
137
 *
138
 * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind
139
 * the scenes for apps using the optional main callbacks. Apps that want to
140
 * use this should just implement SDL_AppIterate directly.
141
 *
142
 * \param appstate an optional pointer, provided by the app in SDL_AppInit.
143
 * \returns SDL_APP_FAILURE to terminate with an error, SDL_APP_SUCCESS to
144
 *          terminate with success, SDL_APP_CONTINUE to continue.
145
 *
146
 * \since This datatype is available since SDL 3.2.0.
147
 */
148
typedef SDL_AppResult (SDLCALL *SDL_AppIterate_func)(void *appstate);
149
150
/**
151
 * Function pointer typedef for SDL_AppEvent.
152
 *
153
 * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind
154
 * the scenes for apps using the optional main callbacks. Apps that want to
155
 * use this should just implement SDL_AppEvent directly.
156
 *
157
 * \param appstate an optional pointer, provided by the app in SDL_AppInit.
158
 * \param event the new event for the app to examine.
159
 * \returns SDL_APP_FAILURE to terminate with an error, SDL_APP_SUCCESS to
160
 *          terminate with success, SDL_APP_CONTINUE to continue.
161
 *
162
 * \since This datatype is available since SDL 3.2.0.
163
 */
164
typedef SDL_AppResult (SDLCALL *SDL_AppEvent_func)(void *appstate, SDL_Event *event);
165
166
/**
167
 * Function pointer typedef for SDL_AppQuit.
168
 *
169
 * These are used by SDL_EnterAppMainCallbacks. This mechanism operates behind
170
 * the scenes for apps using the optional main callbacks. Apps that want to
171
 * use this should just implement SDL_AppEvent directly.
172
 *
173
 * \param appstate an optional pointer, provided by the app in SDL_AppInit.
174
 * \param result the result code that terminated the app (success or failure).
175
 *
176
 * \since This datatype is available since SDL 3.2.0.
177
 */
178
typedef void (SDLCALL *SDL_AppQuit_func)(void *appstate, SDL_AppResult result);
179
180
181
/**
182
 * Initialize the SDL library.
183
 *
184
 * SDL_Init() simply forwards to calling SDL_InitSubSystem(). Therefore, the
185
 * two may be used interchangeably. Though for readability of your code
186
 * SDL_InitSubSystem() might be preferred.
187
 *
188
 * The file I/O (for example: SDL_IOFromFile) and threading (SDL_CreateThread)
189
 * subsystems are initialized by default. Message boxes
190
 * (SDL_ShowSimpleMessageBox) also attempt to work without initializing the
191
 * video subsystem, in hopes of being useful in showing an error dialog when
192
 * SDL_Init fails. You must specifically initialize other subsystems if you
193
 * use them in your application.
194
 *
195
 * Logging (such as SDL_Log) works without initialization, too.
196
 *
197
 * `flags` may be any of the following OR'd together:
198
 *
199
 * - `SDL_INIT_AUDIO`: audio subsystem; automatically initializes the events
200
 *   subsystem
201
 * - `SDL_INIT_VIDEO`: video subsystem; automatically initializes the events
202
 *   subsystem, should be initialized on the main thread.
203
 * - `SDL_INIT_JOYSTICK`: joystick subsystem; automatically initializes the
204
 *   events subsystem
205
 * - `SDL_INIT_HAPTIC`: haptic (force feedback) subsystem
206
 * - `SDL_INIT_GAMEPAD`: gamepad subsystem; automatically initializes the
207
 *   joystick subsystem
208
 * - `SDL_INIT_EVENTS`: events subsystem
209
 * - `SDL_INIT_SENSOR`: sensor subsystem; automatically initializes the events
210
 *   subsystem
211
 * - `SDL_INIT_CAMERA`: camera subsystem; automatically initializes the events
212
 *   subsystem
213
 *
214
 * Subsystem initialization is ref-counted, you must call SDL_QuitSubSystem()
215
 * for each SDL_InitSubSystem() to correctly shutdown a subsystem manually (or
216
 * call SDL_Quit() to force shutdown). If a subsystem is already loaded then
217
 * this call will increase the ref-count and return.
218
 *
219
 * Consider reporting some basic metadata about your application before
220
 * calling SDL_Init, using either SDL_SetAppMetadata() or
221
 * SDL_SetAppMetadataProperty().
222
 *
223
 * \param flags subsystem initialization flags.
224
 * \returns true on success or false on failure; call SDL_GetError() for more
225
 *          information.
226
 *
227
 * \since This function is available since SDL 3.2.0.
228
 *
229
 * \sa SDL_SetAppMetadata
230
 * \sa SDL_SetAppMetadataProperty
231
 * \sa SDL_InitSubSystem
232
 * \sa SDL_Quit
233
 * \sa SDL_SetMainReady
234
 * \sa SDL_WasInit
235
 */
236
extern SDL_DECLSPEC bool SDLCALL SDL_Init(SDL_InitFlags flags);
237
238
/**
239
 * Compatibility function to initialize the SDL library.
240
 *
241
 * This function and SDL_Init() are interchangeable.
242
 *
243
 * \param flags any of the flags used by SDL_Init(); see SDL_Init for details.
244
 * \returns true on success or false on failure; call SDL_GetError() for more
245
 *          information.
246
 *
247
 * \since This function is available since SDL 3.2.0.
248
 *
249
 * \sa SDL_Init
250
 * \sa SDL_Quit
251
 * \sa SDL_QuitSubSystem
252
 */
253
extern SDL_DECLSPEC bool SDLCALL SDL_InitSubSystem(SDL_InitFlags flags);
254
255
/**
256
 * Shut down specific SDL subsystems.
257
 *
258
 * You still need to call SDL_Quit() even if you close all open subsystems
259
 * with SDL_QuitSubSystem().
260
 *
261
 * \param flags any of the flags used by SDL_Init(); see SDL_Init for details.
262
 *
263
 * \since This function is available since SDL 3.2.0.
264
 *
265
 * \sa SDL_InitSubSystem
266
 * \sa SDL_Quit
267
 */
268
extern SDL_DECLSPEC void SDLCALL SDL_QuitSubSystem(SDL_InitFlags flags);
269
270
/**
271
 * Get a mask of the specified subsystems which are currently initialized.
272
 *
273
 * \param flags any of the flags used by SDL_Init(); see SDL_Init for details.
274
 * \returns a mask of all initialized subsystems if `flags` is 0, otherwise it
275
 *          returns the initialization status of the specified subsystems.
276
 *
277
 * \since This function is available since SDL 3.2.0.
278
 *
279
 * \sa SDL_Init
280
 * \sa SDL_InitSubSystem
281
 */
282
extern SDL_DECLSPEC SDL_InitFlags SDLCALL SDL_WasInit(SDL_InitFlags flags);
283
284
/**
285
 * Clean up all initialized subsystems.
286
 *
287
 * You should call this function even if you have already shutdown each
288
 * initialized subsystem with SDL_QuitSubSystem(). It is safe to call this
289
 * function even in the case of errors in initialization.
290
 *
291
 * You can use this function with atexit() to ensure that it is run when your
292
 * application is shutdown, but it is not wise to do this from a library or
293
 * other dynamically loaded code.
294
 *
295
 * \since This function is available since SDL 3.2.0.
296
 *
297
 * \sa SDL_Init
298
 * \sa SDL_QuitSubSystem
299
 */
300
extern SDL_DECLSPEC void SDLCALL SDL_Quit(void);
301
302
/**
303
 * Return whether this is the main thread.
304
 *
305
 * On Apple platforms, the main thread is the thread that runs your program's
306
 * main() entry point. On other platforms, the main thread is the one that
307
 * calls SDL_Init(SDL_INIT_VIDEO), which should usually be the one that runs
308
 * your program's main() entry point. If you are using the main callbacks,
309
 * SDL_AppInit(), SDL_AppIterate(), and SDL_AppQuit() are all called on the
310
 * main thread.
311
 *
312
 * \returns true if this thread is the main thread, or false otherwise.
313
 *
314
 * \threadsafety It is safe to call this function from any thread.
315
 *
316
 * \since This function is available since SDL 3.2.0.
317
 *
318
 * \sa SDL_RunOnMainThread
319
 */
320
extern SDL_DECLSPEC bool SDLCALL SDL_IsMainThread(void);
321
322
/**
323
 * Callback run on the main thread.
324
 *
325
 * \param userdata an app-controlled pointer that is passed to the callback.
326
 *
327
 * \since This datatype is available since SDL 3.2.0.
328
 *
329
 * \sa SDL_RunOnMainThread
330
 */
331
typedef void (SDLCALL *SDL_MainThreadCallback)(void *userdata);
332
333
/**
334
 * Call a function on the main thread during event processing.
335
 *
336
 * If this is called on the main thread, the callback is executed immediately.
337
 * If this is called on another thread, this callback is queued for execution
338
 * on the main thread during event processing.
339
 *
340
 * Be careful of deadlocks when using this functionality. You should not have
341
 * the main thread wait for the current thread while this function is being
342
 * called with `wait_complete` true.
343
 *
344
 * \param callback the callback to call on the main thread.
345
 * \param userdata a pointer that is passed to `callback`.
346
 * \param wait_complete true to wait for the callback to complete, false to
347
 *                      return immediately.
348
 * \returns true on success or false on failure; call SDL_GetError() for more
349
 *          information.
350
 *
351
 * \threadsafety It is safe to call this function from any thread.
352
 *
353
 * \since This function is available since SDL 3.2.0.
354
 *
355
 * \sa SDL_IsMainThread
356
 */
357
extern SDL_DECLSPEC bool SDLCALL SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool wait_complete);
358
359
/**
360
 * Specify basic metadata about your app.
361
 *
362
 * You can optionally provide metadata about your app to SDL. This is not
363
 * required, but strongly encouraged.
364
 *
365
 * There are several locations where SDL can make use of metadata (an "About"
366
 * box in the macOS menu bar, the name of the app can be shown on some audio
367
 * mixers, etc). Any piece of metadata can be left as NULL, if a specific
368
 * detail doesn't make sense for the app.
369
 *
370
 * This function should be called as early as possible, before SDL_Init.
371
 * Multiple calls to this function are allowed, but various state might not
372
 * change once it has been set up with a previous call to this function.
373
 *
374
 * Passing a NULL removes any previous metadata.
375
 *
376
 * This is a simplified interface for the most important information. You can
377
 * supply significantly more detailed metadata with
378
 * SDL_SetAppMetadataProperty().
379
 *
380
 * \param appname The name of the application ("My Game 2: Bad Guy's
381
 *                Revenge!").
382
 * \param appversion The version of the application ("1.0.0beta5" or a git
383
 *                   hash, or whatever makes sense).
384
 * \param appidentifier A unique string in reverse-domain format that
385
 *                      identifies this app ("com.example.mygame2").
386
 * \returns true on success or false on failure; call SDL_GetError() for more
387
 *          information.
388
 *
389
 * \threadsafety It is safe to call this function from any thread.
390
 *
391
 * \since This function is available since SDL 3.2.0.
392
 *
393
 * \sa SDL_SetAppMetadataProperty
394
 */
395
extern SDL_DECLSPEC bool SDLCALL SDL_SetAppMetadata(const char *appname, const char *appversion, const char *appidentifier);
396
397
/**
398
 * Specify metadata about your app through a set of properties.
399
 *
400
 * You can optionally provide metadata about your app to SDL. This is not
401
 * required, but strongly encouraged.
402
 *
403
 * There are several locations where SDL can make use of metadata (an "About"
404
 * box in the macOS menu bar, the name of the app can be shown on some audio
405
 * mixers, etc). Any piece of metadata can be left out, if a specific detail
406
 * doesn't make sense for the app.
407
 *
408
 * This function should be called as early as possible, before SDL_Init.
409
 * Multiple calls to this function are allowed, but various state might not
410
 * change once it has been set up with a previous call to this function.
411
 *
412
 * Once set, this metadata can be read using SDL_GetAppMetadataProperty().
413
 *
414
 * These are the supported properties:
415
 *
416
 * - `SDL_PROP_APP_METADATA_NAME_STRING`: The human-readable name of the
417
 *   application, like "My Game 2: Bad Guy's Revenge!". This will show up
418
 *   anywhere the OS shows the name of the application separately from window
419
 *   titles, such as volume control applets, etc. This defaults to "SDL
420
 *   Application".
421
 * - `SDL_PROP_APP_METADATA_VERSION_STRING`: The version of the app that is
422
 *   running; there are no rules on format, so "1.0.3beta2" and "April 22nd,
423
 *   2024" and a git hash are all valid options. This has no default.
424
 * - `SDL_PROP_APP_METADATA_IDENTIFIER_STRING`: A unique string that
425
 *   identifies this app. This must be in reverse-domain format, like
426
 *   "com.example.mygame2". This string is used by desktop compositors to
427
 *   identify and group windows together, as well as match applications with
428
 *   associated desktop settings and icons. If you plan to package your
429
 *   application in a container such as Flatpak, the app ID should match the
430
 *   name of your Flatpak container as well. This has no default.
431
 * - `SDL_PROP_APP_METADATA_CREATOR_STRING`: The human-readable name of the
432
 *   creator/developer/maker of this app, like "MojoWorkshop, LLC"
433
 * - `SDL_PROP_APP_METADATA_COPYRIGHT_STRING`: The human-readable copyright
434
 *   notice, like "Copyright (c) 2024 MojoWorkshop, LLC" or whatnot. Keep this
435
 *   to one line, don't paste a copy of a whole software license in here. This
436
 *   has no default.
437
 * - `SDL_PROP_APP_METADATA_URL_STRING`: A URL to the app on the web. Maybe a
438
 *   product page, or a storefront, or even a GitHub repository, for user's
439
 *   further information This has no default.
440
 * - `SDL_PROP_APP_METADATA_TYPE_STRING`: The type of application this is.
441
 *   Currently this string can be "game" for a video game, "mediaplayer" for a
442
 *   media player, or generically "application" if nothing else applies.
443
 *   Future versions of SDL might add new types. This defaults to
444
 *   "application".
445
 *
446
 * \param name the name of the metadata property to set.
447
 * \param value the value of the property, or NULL to remove that property.
448
 * \returns true on success or false on failure; call SDL_GetError() for more
449
 *          information.
450
 *
451
 * \threadsafety It is safe to call this function from any thread.
452
 *
453
 * \since This function is available since SDL 3.2.0.
454
 *
455
 * \sa SDL_GetAppMetadataProperty
456
 * \sa SDL_SetAppMetadata
457
 */
458
extern SDL_DECLSPEC bool SDLCALL SDL_SetAppMetadataProperty(const char *name, const char *value);
459
460
#define SDL_PROP_APP_METADATA_NAME_STRING         "SDL.app.metadata.name"
461
#define SDL_PROP_APP_METADATA_VERSION_STRING      "SDL.app.metadata.version"
462
#define SDL_PROP_APP_METADATA_IDENTIFIER_STRING   "SDL.app.metadata.identifier"
463
#define SDL_PROP_APP_METADATA_CREATOR_STRING      "SDL.app.metadata.creator"
464
#define SDL_PROP_APP_METADATA_COPYRIGHT_STRING    "SDL.app.metadata.copyright"
465
#define SDL_PROP_APP_METADATA_URL_STRING          "SDL.app.metadata.url"
466
#define SDL_PROP_APP_METADATA_TYPE_STRING         "SDL.app.metadata.type"
467
468
/**
469
 * Get metadata about your app.
470
 *
471
 * This returns metadata previously set using SDL_SetAppMetadata() or
472
 * SDL_SetAppMetadataProperty(). See SDL_SetAppMetadataProperty() for the list
473
 * of available properties and their meanings.
474
 *
475
 * \param name the name of the metadata property to get.
476
 * \returns the current value of the metadata property, or the default if it
477
 *          is not set, NULL for properties with no default.
478
 *
479
 * \threadsafety It is safe to call this function from any thread, although
480
 *               the string returned is not protected and could potentially be
481
 *               freed if you call SDL_SetAppMetadataProperty() to set that
482
 *               property from another thread.
483
 *
484
 * \since This function is available since SDL 3.2.0.
485
 *
486
 * \sa SDL_SetAppMetadata
487
 * \sa SDL_SetAppMetadataProperty
488
 */
489
extern SDL_DECLSPEC const char * SDLCALL SDL_GetAppMetadataProperty(const char *name);
490
491
/* Ends C function definitions when using C++ */
492
#ifdef __cplusplus
493
}
494
#endif
495
#include <SDL3/SDL_close_code.h>
496
497
#endif /* SDL_init_h_ */
/opt/homebrew/include/SDL3/SDL_keycode.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryKeycode
24
 *
25
 * Defines constants which identify keyboard keys and modifiers.
26
 *
27
 * Please refer to the Best Keyboard Practices document for details on what
28
 * this information means and how best to use it.
29
 *
30
 * https://wiki.libsdl.org/SDL3/BestKeyboardPractices
31
 */
32
33
#ifndef SDL_keycode_h_
34
#define SDL_keycode_h_
35
36
#include <SDL3/SDL_stdinc.h>
37
#include <SDL3/SDL_scancode.h>
38
39
/**
40
 * The SDL virtual key representation.
41
 *
42
 * Values of this type are used to represent keyboard keys using the current
43
 * layout of the keyboard. These values include Unicode values representing
44
 * the unmodified character that would be generated by pressing the key, or an
45
 * `SDLK_*` constant for those keys that do not generate characters.
46
 *
47
 * A special exception is the number keys at the top of the keyboard which map
48
 * to SDLK_0...SDLK_9 on AZERTY layouts.
49
 *
50
 * Keys with the `SDLK_EXTENDED_MASK` bit set do not map to a scancode or
51
 * unicode code point.
52
 *
53
 * \since This datatype is available since SDL 3.2.0.
54
 */
55
typedef Uint32 SDL_Keycode;
56
57
#define SDLK_EXTENDED_MASK          (1u << 29)
58
#define SDLK_SCANCODE_MASK          (1u << 30)
59
#define SDL_SCANCODE_TO_KEYCODE(X)  (X | SDLK_SCANCODE_MASK)
60
#define SDLK_UNKNOWN                0x00000000u /**< 0 */
61
4
#define SDLK_RETURN                 0x0000000du /**< '\r' */
62
0
#define SDLK_ESCAPE                 0x0000001bu /**< '\x1B' */
63
0
#define SDLK_BACKSPACE              0x00000008u /**< '\b' */
64
0
#define SDLK_TAB                    0x00000009u /**< '\t' */
65
0
#define SDLK_SPACE                  0x00000020u /**< ' ' */
66
#define SDLK_EXCLAIM                0x00000021u /**< '!' */
67
#define SDLK_DBLAPOSTROPHE          0x00000022u /**< '"' */
68
#define SDLK_HASH                   0x00000023u /**< '#' */
69
#define SDLK_DOLLAR                 0x00000024u /**< '$' */
70
#define SDLK_PERCENT                0x00000025u /**< '%' */
71
#define SDLK_AMPERSAND              0x00000026u /**< '&' */
72
#define SDLK_APOSTROPHE             0x00000027u /**< '\'' */
73
#define SDLK_LEFTPAREN              0x00000028u /**< '(' */
74
#define SDLK_RIGHTPAREN             0x00000029u /**< ')' */
75
#define SDLK_ASTERISK               0x0000002au /**< '*' */
76
#define SDLK_PLUS                   0x0000002bu /**< '+' */
77
0
#define SDLK_COMMA                  0x0000002cu /**< ',' */
78
#define SDLK_MINUS                  0x0000002du /**< '-' */
79
0
#define SDLK_PERIOD                 0x0000002eu /**< '.' */
80
#define SDLK_SLASH                  0x0000002fu /**< '/' */
81
0
#define SDLK_0                      0x00000030u /**< '0' */
82
4
#define SDLK_1                      0x00000031u /**< '1' */
83
0
#define SDLK_2                      0x00000032u /**< '2' */
84
0
#define SDLK_3                      0x00000033u /**< '3' */
85
0
#define SDLK_4                      0x00000034u /**< '4' */
86
4
#define SDLK_5                      0x00000035u /**< '5' */
87
0
#define SDLK_6                      0x00000036u /**< '6' */
88
0
#define SDLK_7                      0x00000037u /**< '7' */
89
0
#define SDLK_8                      0x00000038u /**< '8' */
90
0
#define SDLK_9                      0x00000039u /**< '9' */
91
#define SDLK_COLON                  0x0000003au /**< ':' */
92
0
#define SDLK_SEMICOLON              0x0000003bu /**< ';' */
93
#define SDLK_LESS                   0x0000003cu /**< '<' */
94
#define SDLK_EQUALS                 0x0000003du /**< '=' */
95
#define SDLK_GREATER                0x0000003eu /**< '>' */
96
#define SDLK_QUESTION               0x0000003fu /**< '?' */
97
#define SDLK_AT                     0x00000040u /**< '@' */
98
#define SDLK_LEFTBRACKET            0x0000005bu /**< '[' */
99
#define SDLK_BACKSLASH              0x0000005cu /**< '\\' */
100
#define SDLK_RIGHTBRACKET           0x0000005du /**< ']' */
101
#define SDLK_CARET                  0x0000005eu /**< '^' */
102
#define SDLK_UNDERSCORE             0x0000005fu /**< '_' */
103
#define SDLK_GRAVE                  0x00000060u /**< '`' */
104
0
#define SDLK_A                      0x00000061u /**< 'a' */
105
0
#define SDLK_B                      0x00000062u /**< 'b' */
106
0
#define SDLK_C                      0x00000063u /**< 'c' */
107
0
#define SDLK_D                      0x00000064u /**< 'd' */
108
0
#define SDLK_E                      0x00000065u /**< 'e' */
109
0
#define SDLK_F                      0x00000066u /**< 'f' */
110
0
#define SDLK_G                      0x00000067u /**< 'g' */
111
0
#define SDLK_H                      0x00000068u /**< 'h' */
112
0
#define SDLK_I                      0x00000069u /**< 'i' */
113
0
#define SDLK_J                      0x0000006au /**< 'j' */
114
0
#define SDLK_K                      0x0000006bu /**< 'k' */
115
0
#define SDLK_L                      0x0000006cu /**< 'l' */
116
0
#define SDLK_M                      0x0000006du /**< 'm' */
117
0
#define SDLK_N                      0x0000006eu /**< 'n' */
118
0
#define SDLK_O                      0x0000006fu /**< 'o' */
119
0
#define SDLK_P                      0x00000070u /**< 'p' */
120
0
#define SDLK_Q                      0x00000071u /**< 'q' */
121
0
#define SDLK_R                      0x00000072u /**< 'r' */
122
0
#define SDLK_S                      0x00000073u /**< 's' */
123
0
#define SDLK_T                      0x00000074u /**< 't' */
124
0
#define SDLK_U                      0x00000075u /**< 'u' */
125
0
#define SDLK_V                      0x00000076u /**< 'v' */
126
0
#define SDLK_W                      0x00000077u /**< 'w' */
127
0
#define SDLK_X                      0x00000078u /**< 'x' */
128
0
#define SDLK_Y                      0x00000079u /**< 'y' */
129
0
#define SDLK_Z                      0x0000007au /**< 'z' */
130
#define SDLK_LEFTBRACE              0x0000007bu /**< '{' */
131
#define SDLK_PIPE                   0x0000007cu /**< '|' */
132
#define SDLK_RIGHTBRACE             0x0000007du /**< '}' */
133
#define SDLK_TILDE                  0x0000007eu /**< '~' */
134
0
#define SDLK_DELETE                 0x0000007fu /**< '\x7F' */
135
#define SDLK_PLUSMINUS              0x000000b1u /**< '\xB1' */
136
0
#define SDLK_CAPSLOCK               0x40000039u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CAPSLOCK) */
137
0
#define SDLK_F1                     0x4000003au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F1) */
138
0
#define SDLK_F2                     0x4000003bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F2) */
139
0
#define SDLK_F3                     0x4000003cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F3) */
140
0
#define SDLK_F4                     0x4000003du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F4) */
141
0
#define SDLK_F5                     0x4000003eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F5) */
142
0
#define SDLK_F6                     0x4000003fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F6) */
143
0
#define SDLK_F7                     0x40000040u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F7) */
144
0
#define SDLK_F8                     0x40000041u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F8) */
145
0
#define SDLK_F9                     0x40000042u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F9) */
146
0
#define SDLK_F10                    0x40000043u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F10) */
147
0
#define SDLK_F11                    0x40000044u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F11) */
148
0
#define SDLK_F12                    0x40000045u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F12) */
149
0
#define SDLK_PRINTSCREEN            0x40000046u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRINTSCREEN) */
150
0
#define SDLK_SCROLLLOCK             0x40000047u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SCROLLLOCK) */
151
0
#define SDLK_PAUSE                  0x40000048u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAUSE) */
152
0
#define SDLK_INSERT                 0x40000049u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_INSERT) */
153
0
#define SDLK_HOME                   0x4000004au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HOME) */
154
0
#define SDLK_PAGEUP                 0x4000004bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEUP) */
155
0
#define SDLK_END                    0x4000004du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_END) */
156
0
#define SDLK_PAGEDOWN               0x4000004eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PAGEDOWN) */
157
1.12k
#define SDLK_RIGHT                  0x4000004fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RIGHT) */
158
0
#define SDLK_LEFT                   0x40000050u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LEFT) */
159
626
#define SDLK_DOWN                   0x40000051u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DOWN) */
160
788
#define SDLK_UP                     0x40000052u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UP) */
161
0
#define SDLK_NUMLOCKCLEAR           0x40000053u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_NUMLOCKCLEAR) */
162
#define SDLK_KP_DIVIDE              0x40000054u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DIVIDE) */
163
#define SDLK_KP_MULTIPLY            0x40000055u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MULTIPLY) */
164
#define SDLK_KP_MINUS               0x40000056u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MINUS) */
165
#define SDLK_KP_PLUS                0x40000057u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUS) */
166
2
#define SDLK_KP_ENTER               0x40000058u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_ENTER) */
167
#define SDLK_KP_1                   0x40000059u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_1) */
168
#define SDLK_KP_2                   0x4000005au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_2) */
169
#define SDLK_KP_3                   0x4000005bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_3) */
170
#define SDLK_KP_4                   0x4000005cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_4) */
171
#define SDLK_KP_5                   0x4000005du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_5) */
172
#define SDLK_KP_6                   0x4000005eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_6) */
173
#define SDLK_KP_7                   0x4000005fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_7) */
174
#define SDLK_KP_8                   0x40000060u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_8) */
175
#define SDLK_KP_9                   0x40000061u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_9) */
176
#define SDLK_KP_0                   0x40000062u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_0) */
177
#define SDLK_KP_PERIOD              0x40000063u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERIOD) */
178
0
#define SDLK_APPLICATION            0x40000065u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_APPLICATION) */
179
#define SDLK_POWER                  0x40000066u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_POWER) */
180
#define SDLK_KP_EQUALS              0x40000067u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALS) */
181
0
#define SDLK_F13                    0x40000068u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F13) */
182
0
#define SDLK_F14                    0x40000069u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F14) */
183
0
#define SDLK_F15                    0x4000006au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F15) */
184
0
#define SDLK_F16                    0x4000006bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F16) */
185
0
#define SDLK_F17                    0x4000006cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F17) */
186
0
#define SDLK_F18                    0x4000006du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F18) */
187
0
#define SDLK_F19                    0x4000006eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F19) */
188
0
#define SDLK_F20                    0x4000006fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F20) */
189
0
#define SDLK_F21                    0x40000070u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F21) */
190
0
#define SDLK_F22                    0x40000071u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F22) */
191
0
#define SDLK_F23                    0x40000072u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F23) */
192
0
#define SDLK_F24                    0x40000073u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_F24) */
193
#define SDLK_EXECUTE                0x40000074u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXECUTE) */
194
#define SDLK_HELP                   0x40000075u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_HELP) */
195
#define SDLK_MENU                   0x40000076u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MENU) */
196
#define SDLK_SELECT                 0x40000077u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SELECT) */
197
#define SDLK_STOP                   0x40000078u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_STOP) */
198
#define SDLK_AGAIN                  0x40000079u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AGAIN) */
199
#define SDLK_UNDO                   0x4000007au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_UNDO) */
200
#define SDLK_CUT                    0x4000007bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CUT) */
201
#define SDLK_COPY                   0x4000007cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_COPY) */
202
#define SDLK_PASTE                  0x4000007du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PASTE) */
203
#define SDLK_FIND                   0x4000007eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_FIND) */
204
#define SDLK_MUTE                   0x4000007fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MUTE) */
205
#define SDLK_VOLUMEUP               0x40000080u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEUP) */
206
#define SDLK_VOLUMEDOWN             0x40000081u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_VOLUMEDOWN) */
207
#define SDLK_KP_COMMA               0x40000085u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COMMA) */
208
#define SDLK_KP_EQUALSAS400         0x40000086u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EQUALSAS400) */
209
#define SDLK_ALTERASE               0x40000099u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_ALTERASE) */
210
#define SDLK_SYSREQ                 0x4000009au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SYSREQ) */
211
#define SDLK_CANCEL                 0x4000009bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CANCEL) */
212
#define SDLK_CLEAR                  0x4000009cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEAR) */
213
#define SDLK_PRIOR                  0x4000009du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_PRIOR) */
214
#define SDLK_RETURN2                0x4000009eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RETURN2) */
215
#define SDLK_SEPARATOR              0x4000009fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SEPARATOR) */
216
#define SDLK_OUT                    0x400000a0u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OUT) */
217
#define SDLK_OPER                   0x400000a1u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_OPER) */
218
#define SDLK_CLEARAGAIN             0x400000a2u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CLEARAGAIN) */
219
#define SDLK_CRSEL                  0x400000a3u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CRSEL) */
220
#define SDLK_EXSEL                  0x400000a4u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_EXSEL) */
221
#define SDLK_KP_00                  0x400000b0u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_00) */
222
#define SDLK_KP_000                 0x400000b1u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_000) */
223
#define SDLK_THOUSANDSSEPARATOR     0x400000b2u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_THOUSANDSSEPARATOR) */
224
#define SDLK_DECIMALSEPARATOR       0x400000b3u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_DECIMALSEPARATOR) */
225
#define SDLK_CURRENCYUNIT           0x400000b4u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYUNIT) */
226
#define SDLK_CURRENCYSUBUNIT        0x400000b5u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CURRENCYSUBUNIT) */
227
#define SDLK_KP_LEFTPAREN           0x400000b6u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTPAREN) */
228
#define SDLK_KP_RIGHTPAREN          0x400000b7u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTPAREN) */
229
#define SDLK_KP_LEFTBRACE           0x400000b8u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LEFTBRACE) */
230
#define SDLK_KP_RIGHTBRACE          0x400000b9u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_RIGHTBRACE) */
231
#define SDLK_KP_TAB                 0x400000bau /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_TAB) */
232
#define SDLK_KP_BACKSPACE           0x400000bbu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BACKSPACE) */
233
#define SDLK_KP_A                   0x400000bcu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_A) */
234
#define SDLK_KP_B                   0x400000bdu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_B) */
235
#define SDLK_KP_C                   0x400000beu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_C) */
236
#define SDLK_KP_D                   0x400000bfu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_D) */
237
#define SDLK_KP_E                   0x400000c0u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_E) */
238
#define SDLK_KP_F                   0x400000c1u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_F) */
239
#define SDLK_KP_XOR                 0x400000c2u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_XOR) */
240
#define SDLK_KP_POWER               0x400000c3u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_POWER) */
241
#define SDLK_KP_PERCENT             0x400000c4u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PERCENT) */
242
#define SDLK_KP_LESS                0x400000c5u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_LESS) */
243
#define SDLK_KP_GREATER             0x400000c6u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_GREATER) */
244
#define SDLK_KP_AMPERSAND           0x400000c7u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AMPERSAND) */
245
#define SDLK_KP_DBLAMPERSAND        0x400000c8u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLAMPERSAND) */
246
#define SDLK_KP_VERTICALBAR         0x400000c9u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_VERTICALBAR) */
247
#define SDLK_KP_DBLVERTICALBAR      0x400000cau /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DBLVERTICALBAR) */
248
#define SDLK_KP_COLON               0x400000cbu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_COLON) */
249
#define SDLK_KP_HASH                0x400000ccu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HASH) */
250
#define SDLK_KP_SPACE               0x400000cdu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_SPACE) */
251
#define SDLK_KP_AT                  0x400000ceu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_AT) */
252
#define SDLK_KP_EXCLAM              0x400000cfu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_EXCLAM) */
253
#define SDLK_KP_MEMSTORE            0x400000d0u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSTORE) */
254
#define SDLK_KP_MEMRECALL           0x400000d1u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMRECALL) */
255
#define SDLK_KP_MEMCLEAR            0x400000d2u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMCLEAR) */
256
#define SDLK_KP_MEMADD              0x400000d3u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMADD) */
257
#define SDLK_KP_MEMSUBTRACT         0x400000d4u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMSUBTRACT) */
258
#define SDLK_KP_MEMMULTIPLY         0x400000d5u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMMULTIPLY) */
259
#define SDLK_KP_MEMDIVIDE           0x400000d6u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_MEMDIVIDE) */
260
#define SDLK_KP_PLUSMINUS           0x400000d7u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_PLUSMINUS) */
261
#define SDLK_KP_CLEAR               0x400000d8u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEAR) */
262
#define SDLK_KP_CLEARENTRY          0x400000d9u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_CLEARENTRY) */
263
#define SDLK_KP_BINARY              0x400000dau /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_BINARY) */
264
#define SDLK_KP_OCTAL               0x400000dbu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_OCTAL) */
265
#define SDLK_KP_DECIMAL             0x400000dcu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_DECIMAL) */
266
#define SDLK_KP_HEXADECIMAL         0x400000ddu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_KP_HEXADECIMAL) */
267
0
#define SDLK_LCTRL                  0x400000e0u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LCTRL) */
268
0
#define SDLK_LSHIFT                 0x400000e1u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LSHIFT) */
269
432
#define SDLK_LALT                   0x400000e2u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LALT) */
270
0
#define SDLK_LGUI                   0x400000e3u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LGUI) */
271
0
#define SDLK_RCTRL                  0x400000e4u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RCTRL) */
272
0
#define SDLK_RSHIFT                 0x400000e5u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RSHIFT) */
273
0
#define SDLK_RALT                   0x400000e6u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RALT) */
274
0
#define SDLK_RGUI                   0x400000e7u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_RGUI) */
275
#define SDLK_MODE                   0x40000101u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MODE) */
276
#define SDLK_SLEEP                  0x40000102u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SLEEP) */
277
#define SDLK_WAKE                   0x40000103u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_WAKE) */
278
#define SDLK_CHANNEL_INCREMENT      0x40000104u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CHANNEL_INCREMENT) */
279
#define SDLK_CHANNEL_DECREMENT      0x40000105u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CHANNEL_DECREMENT) */
280
#define SDLK_MEDIA_PLAY             0x40000106u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_PLAY) */
281
#define SDLK_MEDIA_PAUSE            0x40000107u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_PAUSE) */
282
#define SDLK_MEDIA_RECORD           0x40000108u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_RECORD) */
283
#define SDLK_MEDIA_FAST_FORWARD     0x40000109u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_FAST_FORWARD) */
284
#define SDLK_MEDIA_REWIND           0x4000010au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_REWIND) */
285
#define SDLK_MEDIA_NEXT_TRACK       0x4000010bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_NEXT_TRACK) */
286
#define SDLK_MEDIA_PREVIOUS_TRACK   0x4000010cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_PREVIOUS_TRACK) */
287
#define SDLK_MEDIA_STOP             0x4000010du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_STOP) */
288
#define SDLK_MEDIA_EJECT            0x4000010eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_EJECT) */
289
#define SDLK_MEDIA_PLAY_PAUSE       0x4000010fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_PLAY_PAUSE) */
290
#define SDLK_MEDIA_SELECT           0x40000110u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_MEDIA_SELECT) */
291
#define SDLK_AC_NEW                 0x40000111u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_NEW) */
292
#define SDLK_AC_OPEN                0x40000112u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_OPEN) */
293
#define SDLK_AC_CLOSE               0x40000113u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_CLOSE) */
294
#define SDLK_AC_EXIT                0x40000114u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_EXIT) */
295
#define SDLK_AC_SAVE                0x40000115u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_SAVE) */
296
#define SDLK_AC_PRINT               0x40000116u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_PRINT) */
297
#define SDLK_AC_PROPERTIES          0x40000117u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_PROPERTIES) */
298
#define SDLK_AC_SEARCH              0x40000118u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_SEARCH) */
299
#define SDLK_AC_HOME                0x40000119u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_HOME) */
300
0
#define SDLK_AC_BACK                0x4000011au /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BACK) */
301
0
#define SDLK_AC_FORWARD             0x4000011bu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_FORWARD) */
302
#define SDLK_AC_STOP                0x4000011cu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_STOP) */
303
#define SDLK_AC_REFRESH             0x4000011du /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_REFRESH) */
304
#define SDLK_AC_BOOKMARKS           0x4000011eu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_AC_BOOKMARKS) */
305
#define SDLK_SOFTLEFT               0x4000011fu /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SOFTLEFT) */
306
#define SDLK_SOFTRIGHT              0x40000120u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SOFTRIGHT) */
307
#define SDLK_CALL                   0x40000121u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CALL) */
308
#define SDLK_ENDCALL                0x40000122u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_ENDCALL) */
309
#define SDLK_LEFT_TAB               0x20000001u /**< Extended key Left Tab */
310
#define SDLK_LEVEL5_SHIFT           0x20000002u /**< Extended key Level 5 Shift */
311
#define SDLK_MULTI_KEY_COMPOSE      0x20000003u /**< Extended key Multi-key Compose */
312
#define SDLK_LMETA                  0x20000004u /**< Extended key Left Meta */
313
#define SDLK_RMETA                  0x20000005u /**< Extended key Right Meta */
314
#define SDLK_LHYPER                 0x20000006u /**< Extended key Left Hyper */
315
#define SDLK_RHYPER                 0x20000007u /**< Extended key Right Hyper */
316
317
/**
318
 * Valid key modifiers (possibly OR'd together).
319
 *
320
 * \since This datatype is available since SDL 3.2.0.
321
 */
322
typedef Uint16 SDL_Keymod;
323
324
#define SDL_KMOD_NONE   0x0000u /**< no modifier is applicable. */
325
1.49k
#define SDL_KMOD_LSHIFT 0x0001u /**< the left Shift key is down. */
326
1.49k
#define SDL_KMOD_RSHIFT 0x0002u /**< the right Shift key is down. */
327
#define SDL_KMOD_LEVEL5 0x0004u /**< the Level 5 Shift key is down. */
328
1.49k
#define SDL_KMOD_LCTRL  0x0040u /**< the left Ctrl (Control) key is down. */
329
1.49k
#define SDL_KMOD_RCTRL  0x0080u /**< the right Ctrl (Control) key is down. */
330
1.49k
#define SDL_KMOD_LALT   0x0100u /**< the left Alt key is down. */
331
1.49k
#define SDL_KMOD_RALT   0x0200u /**< the right Alt key is down. */
332
1.49k
#define SDL_KMOD_LGUI   0x0400u /**< the left GUI key (often the Windows key) is down. */
333
1.49k
#define SDL_KMOD_RGUI   0x0800u /**< the right GUI key (often the Windows key) is down. */
334
#define SDL_KMOD_NUM    0x1000u /**< the Num Lock key (may be located on an extended keypad) is down. */
335
#define SDL_KMOD_CAPS   0x2000u /**< the Caps Lock key is down. */
336
#define SDL_KMOD_MODE   0x4000u /**< the !AltGr key is down. */
337
#define SDL_KMOD_SCROLL 0x8000u /**< the Scroll Lock key is down. */
338
1.49k
#define SDL_KMOD_CTRL   (SDL_KMOD_LCTRL | SDL_KMOD_RCTRL)   /**< Any Ctrl key is down. */
339
1.49k
#define SDL_KMOD_SHIFT  (SDL_KMOD_LSHIFT | SDL_KMOD_RSHIFT) /**< Any Shift key is down. */
340
1.49k
#define SDL_KMOD_ALT    (SDL_KMOD_LALT | SDL_KMOD_RALT)     /**< Any Alt key is down. */
341
1.49k
#define SDL_KMOD_GUI    (SDL_KMOD_LGUI | SDL_KMOD_RGUI)     /**< Any GUI key is down. */
342
343
#endif /* SDL_keycode_h_ */
/opt/homebrew/include/SDL3/SDL_mouse.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryMouse
24
 *
25
 * Any GUI application has to deal with the mouse, and SDL provides functions
26
 * to manage mouse input and the displayed cursor.
27
 *
28
 * Most interactions with the mouse will come through the event subsystem.
29
 * Moving a mouse generates an SDL_EVENT_MOUSE_MOTION event, pushing a button
30
 * generates SDL_EVENT_MOUSE_BUTTON_DOWN, etc, but one can also query the
31
 * current state of the mouse at any time with SDL_GetMouseState().
32
 *
33
 * For certain games, it's useful to disassociate the mouse cursor from mouse
34
 * input. An FPS, for example, would not want the player's motion to stop as
35
 * the mouse hits the edge of the window. For these scenarios, use
36
 * SDL_SetWindowRelativeMouseMode(), which hides the cursor, grabs mouse input
37
 * to the window, and reads mouse input no matter how far it moves.
38
 *
39
 * Games that want the system to track the mouse but want to draw their own
40
 * cursor can use SDL_HideCursor() and SDL_ShowCursor(). It might be more
41
 * efficient to let the system manage the cursor, if possible, using
42
 * SDL_SetCursor() with a custom image made through SDL_CreateColorCursor(),
43
 * or perhaps just a specific system cursor from SDL_CreateSystemCursor().
44
 *
45
 * SDL can, on many platforms, differentiate between multiple connected mice,
46
 * allowing for interesting input scenarios and multiplayer games. They can be
47
 * enumerated with SDL_GetMice(), and SDL will send SDL_EVENT_MOUSE_ADDED and
48
 * SDL_EVENT_MOUSE_REMOVED events as they are connected and unplugged.
49
 *
50
 * Since many apps only care about basic mouse input, SDL offers a virtual
51
 * mouse device for touch and pen input, which often can make a desktop
52
 * application work on a touchscreen phone without any code changes. Apps that
53
 * care about touch/pen separately from mouse input should filter out events
54
 * with a `which` field of SDL_TOUCH_MOUSEID/SDL_PEN_MOUSEID.
55
 */
56
57
#ifndef SDL_mouse_h_
58
#define SDL_mouse_h_
59
60
#include <SDL3/SDL_stdinc.h>
61
#include <SDL3/SDL_error.h>
62
#include <SDL3/SDL_surface.h>
63
#include <SDL3/SDL_video.h>
64
65
#include <SDL3/SDL_begin_code.h>
66
/* Set up for C function definitions, even when using C++ */
67
#ifdef __cplusplus
68
extern "C" {
69
#endif
70
71
/**
72
 * This is a unique ID for a mouse for the time it is connected to the system,
73
 * and is never reused for the lifetime of the application.
74
 *
75
 * If the mouse is disconnected and reconnected, it will get a new ID.
76
 *
77
 * The value 0 is an invalid ID.
78
 *
79
 * \since This datatype is available since SDL 3.2.0.
80
 */
81
typedef Uint32 SDL_MouseID;
82
83
/**
84
 * The structure used to identify an SDL cursor.
85
 *
86
 * This is opaque data.
87
 *
88
 * \since This struct is available since SDL 3.2.0.
89
 */
90
typedef struct SDL_Cursor SDL_Cursor;
91
92
/**
93
 * Cursor types for SDL_CreateSystemCursor().
94
 *
95
 * \since This enum is available since SDL 3.2.0.
96
 */
97
typedef enum SDL_SystemCursor
98
{
99
    SDL_SYSTEM_CURSOR_DEFAULT,      /**< Default cursor. Usually an arrow. */
100
    SDL_SYSTEM_CURSOR_TEXT,         /**< Text selection. Usually an I-beam. */
101
    SDL_SYSTEM_CURSOR_WAIT,         /**< Wait. Usually an hourglass or watch or spinning ball. */
102
    SDL_SYSTEM_CURSOR_CROSSHAIR,    /**< Crosshair. */
103
    SDL_SYSTEM_CURSOR_PROGRESS,     /**< Program is busy but still interactive. Usually it's WAIT with an arrow. */
104
    SDL_SYSTEM_CURSOR_NWSE_RESIZE,  /**< Double arrow pointing northwest and southeast. */
105
    SDL_SYSTEM_CURSOR_NESW_RESIZE,  /**< Double arrow pointing northeast and southwest. */
106
    SDL_SYSTEM_CURSOR_EW_RESIZE,    /**< Double arrow pointing west and east. */
107
    SDL_SYSTEM_CURSOR_NS_RESIZE,    /**< Double arrow pointing north and south. */
108
    SDL_SYSTEM_CURSOR_MOVE,         /**< Four pointed arrow pointing north, south, east, and west. */
109
    SDL_SYSTEM_CURSOR_NOT_ALLOWED,  /**< Not permitted. Usually a slashed circle or crossbones. */
110
    SDL_SYSTEM_CURSOR_POINTER,      /**< Pointer that indicates a link. Usually a pointing hand. */
111
    SDL_SYSTEM_CURSOR_NW_RESIZE,    /**< Window resize top-left. This may be a single arrow or a double arrow like NWSE_RESIZE. */
112
    SDL_SYSTEM_CURSOR_N_RESIZE,     /**< Window resize top. May be NS_RESIZE. */
113
    SDL_SYSTEM_CURSOR_NE_RESIZE,    /**< Window resize top-right. May be NESW_RESIZE. */
114
    SDL_SYSTEM_CURSOR_E_RESIZE,     /**< Window resize right. May be EW_RESIZE. */
115
    SDL_SYSTEM_CURSOR_SE_RESIZE,    /**< Window resize bottom-right. May be NWSE_RESIZE. */
116
    SDL_SYSTEM_CURSOR_S_RESIZE,     /**< Window resize bottom. May be NS_RESIZE. */
117
    SDL_SYSTEM_CURSOR_SW_RESIZE,    /**< Window resize bottom-left. May be NESW_RESIZE. */
118
    SDL_SYSTEM_CURSOR_W_RESIZE,     /**< Window resize left. May be EW_RESIZE. */
119
    SDL_SYSTEM_CURSOR_COUNT
120
} SDL_SystemCursor;
121
122
/**
123
 * Scroll direction types for the Scroll event
124
 *
125
 * \since This enum is available since SDL 3.2.0.
126
 */
127
typedef enum SDL_MouseWheelDirection
128
{
129
    SDL_MOUSEWHEEL_NORMAL,    /**< The scroll direction is normal */
130
    SDL_MOUSEWHEEL_FLIPPED    /**< The scroll direction is flipped / natural */
131
} SDL_MouseWheelDirection;
132
133
/**
134
 * A bitmask of pressed mouse buttons, as reported by SDL_GetMouseState, etc.
135
 *
136
 * - Button 1: Left mouse button
137
 * - Button 2: Middle mouse button
138
 * - Button 3: Right mouse button
139
 * - Button 4: Side mouse button 1
140
 * - Button 5: Side mouse button 2
141
 *
142
 * \since This datatype is available since SDL 3.2.0.
143
 *
144
 * \sa SDL_GetMouseState
145
 * \sa SDL_GetGlobalMouseState
146
 * \sa SDL_GetRelativeMouseState
147
 */
148
typedef Uint32 SDL_MouseButtonFlags;
149
150
4
#define SDL_BUTTON_LEFT     1
151
4
#define SDL_BUTTON_MIDDLE   2
152
4
#define SDL_BUTTON_RIGHT    3
153
4
#define SDL_BUTTON_X1       4
154
4
#define SDL_BUTTON_X2       5
155
156
#define SDL_BUTTON_MASK(X)  (1u << ((X)-1))
157
#define SDL_BUTTON_LMASK    SDL_BUTTON_MASK(SDL_BUTTON_LEFT)
158
#define SDL_BUTTON_MMASK    SDL_BUTTON_MASK(SDL_BUTTON_MIDDLE)
159
#define SDL_BUTTON_RMASK    SDL_BUTTON_MASK(SDL_BUTTON_RIGHT)
160
#define SDL_BUTTON_X1MASK   SDL_BUTTON_MASK(SDL_BUTTON_X1)
161
#define SDL_BUTTON_X2MASK   SDL_BUTTON_MASK(SDL_BUTTON_X2)
162
163
164
/* Function prototypes */
165
166
/**
167
 * Return whether a mouse is currently connected.
168
 *
169
 * \returns true if a mouse is connected, false otherwise.
170
 *
171
 * \threadsafety This function should only be called on the main thread.
172
 *
173
 * \since This function is available since SDL 3.2.0.
174
 *
175
 * \sa SDL_GetMice
176
 */
177
extern SDL_DECLSPEC bool SDLCALL SDL_HasMouse(void);
178
179
/**
180
 * Get a list of currently connected mice.
181
 *
182
 * Note that this will include any device or virtual driver that includes
183
 * mouse functionality, including some game controllers, KVM switches, etc.
184
 * You should wait for input from a device before you consider it actively in
185
 * use.
186
 *
187
 * \param count a pointer filled in with the number of mice returned, may be
188
 *              NULL.
189
 * \returns a 0 terminated array of mouse instance IDs or NULL on failure;
190
 *          call SDL_GetError() for more information. This should be freed
191
 *          with SDL_free() when it is no longer needed.
192
 *
193
 * \threadsafety This function should only be called on the main thread.
194
 *
195
 * \since This function is available since SDL 3.2.0.
196
 *
197
 * \sa SDL_GetMouseNameForID
198
 * \sa SDL_HasMouse
199
 */
200
extern SDL_DECLSPEC SDL_MouseID * SDLCALL SDL_GetMice(int *count);
201
202
/**
203
 * Get the name of a mouse.
204
 *
205
 * This function returns "" if the mouse doesn't have a name.
206
 *
207
 * \param instance_id the mouse instance ID.
208
 * \returns the name of the selected mouse, or NULL on failure; call
209
 *          SDL_GetError() for more information.
210
 *
211
 * \threadsafety This function should only be called on the main thread.
212
 *
213
 * \since This function is available since SDL 3.2.0.
214
 *
215
 * \sa SDL_GetMice
216
 */
217
extern SDL_DECLSPEC const char * SDLCALL SDL_GetMouseNameForID(SDL_MouseID instance_id);
218
219
/**
220
 * Get the window which currently has mouse focus.
221
 *
222
 * \returns the window with mouse focus.
223
 *
224
 * \threadsafety This function should only be called on the main thread.
225
 *
226
 * \since This function is available since SDL 3.2.0.
227
 */
228
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetMouseFocus(void);
229
230
/**
231
 * Query SDL's cache for the synchronous mouse button state and the
232
 * window-relative SDL-cursor position.
233
 *
234
 * This function returns the cached synchronous state as SDL understands it
235
 * from the last pump of the event queue.
236
 *
237
 * To query the platform for immediate asynchronous state, use
238
 * SDL_GetGlobalMouseState.
239
 *
240
 * Passing non-NULL pointers to `x` or `y` will write the destination with
241
 * respective x or y coordinates relative to the focused window.
242
 *
243
 * In Relative Mode, the SDL-cursor's position usually contradicts the
244
 * platform-cursor's position as manually calculated from
245
 * SDL_GetGlobalMouseState() and SDL_GetWindowPosition.
246
 *
247
 * \param x a pointer to receive the SDL-cursor's x-position from the focused
248
 *          window's top left corner, can be NULL if unused.
249
 * \param y a pointer to receive the SDL-cursor's y-position from the focused
250
 *          window's top left corner, can be NULL if unused.
251
 * \returns a 32-bit bitmask of the button state that can be bitwise-compared
252
 *          against the SDL_BUTTON_MASK(X) macro.
253
 *
254
 * \threadsafety This function should only be called on the main thread.
255
 *
256
 * \since This function is available since SDL 3.2.0.
257
 *
258
 * \sa SDL_GetGlobalMouseState
259
 * \sa SDL_GetRelativeMouseState
260
 */
261
extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetMouseState(float *x, float *y);
262
263
/**
264
 * Query the platform for the asynchronous mouse button state and the
265
 * desktop-relative platform-cursor position.
266
 *
267
 * This function immediately queries the platform for the most recent
268
 * asynchronous state, more costly than retrieving SDL's cached state in
269
 * SDL_GetMouseState().
270
 *
271
 * Passing non-NULL pointers to `x` or `y` will write the destination with
272
 * respective x or y coordinates relative to the desktop.
273
 *
274
 * In Relative Mode, the platform-cursor's position usually contradicts the
275
 * SDL-cursor's position as manually calculated from SDL_GetMouseState() and
276
 * SDL_GetWindowPosition.
277
 *
278
 * This function can be useful if you need to track the mouse outside of a
279
 * specific window and SDL_CaptureMouse() doesn't fit your needs. For example,
280
 * it could be useful if you need to track the mouse while dragging a window,
281
 * where coordinates relative to a window might not be in sync at all times.
282
 *
283
 * \param x a pointer to receive the platform-cursor's x-position from the
284
 *          desktop's top left corner, can be NULL if unused.
285
 * \param y a pointer to receive the platform-cursor's y-position from the
286
 *          desktop's top left corner, can be NULL if unused.
287
 * \returns a 32-bit bitmask of the button state that can be bitwise-compared
288
 *          against the SDL_BUTTON_MASK(X) macro.
289
 *
290
 * \threadsafety This function should only be called on the main thread.
291
 *
292
 * \since This function is available since SDL 3.2.0.
293
 *
294
 * \sa SDL_CaptureMouse
295
 * \sa SDL_GetMouseState
296
 * \sa SDL_GetGlobalMouseState
297
 */
298
extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetGlobalMouseState(float *x, float *y);
299
300
/**
301
 * Query SDL's cache for the synchronous mouse button state and accumulated
302
 * mouse delta since last call.
303
 *
304
 * This function returns the cached synchronous state as SDL understands it
305
 * from the last pump of the event queue.
306
 *
307
 * To query the platform for immediate asynchronous state, use
308
 * SDL_GetGlobalMouseState.
309
 *
310
 * Passing non-NULL pointers to `x` or `y` will write the destination with
311
 * respective x or y deltas accumulated since the last call to this function
312
 * (or since event initialization).
313
 *
314
 * This function is useful for reducing overhead by processing relative mouse
315
 * inputs in one go per-frame instead of individually per-event, at the
316
 * expense of losing the order between events within the frame (e.g. quickly
317
 * pressing and releasing a button within the same frame).
318
 *
319
 * \param x a pointer to receive the x mouse delta accumulated since last
320
 *          call, can be NULL if unused.
321
 * \param y a pointer to receive the y mouse delta accumulated since last
322
 *          call, can be NULL if unused.
323
 * \returns a 32-bit bitmask of the button state that can be bitwise-compared
324
 *          against the SDL_BUTTON_MASK(X) macro.
325
 *
326
 * \threadsafety This function should only be called on the main thread.
327
 *
328
 * \since This function is available since SDL 3.2.0.
329
 *
330
 * \sa SDL_GetMouseState
331
 * \sa SDL_GetGlobalMouseState
332
 */
333
extern SDL_DECLSPEC SDL_MouseButtonFlags SDLCALL SDL_GetRelativeMouseState(float *x, float *y);
334
335
/**
336
 * Move the mouse cursor to the given position within the window.
337
 *
338
 * This function generates a mouse motion event if relative mode is not
339
 * enabled. If relative mode is enabled, you can force mouse events for the
340
 * warp by setting the SDL_HINT_MOUSE_RELATIVE_WARP_MOTION hint.
341
 *
342
 * Note that this function will appear to succeed, but not actually move the
343
 * mouse when used over Microsoft Remote Desktop.
344
 *
345
 * \param window the window to move the mouse into, or NULL for the current
346
 *               mouse focus.
347
 * \param x the x coordinate within the window.
348
 * \param y the y coordinate within the window.
349
 *
350
 * \threadsafety This function should only be called on the main thread.
351
 *
352
 * \since This function is available since SDL 3.2.0.
353
 *
354
 * \sa SDL_WarpMouseGlobal
355
 */
356
extern SDL_DECLSPEC void SDLCALL SDL_WarpMouseInWindow(SDL_Window *window,
357
                                                   float x, float y);
358
359
/**
360
 * Move the mouse to the given position in global screen space.
361
 *
362
 * This function generates a mouse motion event.
363
 *
364
 * A failure of this function usually means that it is unsupported by a
365
 * platform.
366
 *
367
 * Note that this function will appear to succeed, but not actually move the
368
 * mouse when used over Microsoft Remote Desktop.
369
 *
370
 * \param x the x coordinate.
371
 * \param y the y coordinate.
372
 * \returns true on success or false on failure; call SDL_GetError() for more
373
 *          information.
374
 *
375
 * \threadsafety This function should only be called on the main thread.
376
 *
377
 * \since This function is available since SDL 3.2.0.
378
 *
379
 * \sa SDL_WarpMouseInWindow
380
 */
381
extern SDL_DECLSPEC bool SDLCALL SDL_WarpMouseGlobal(float x, float y);
382
383
/**
384
 * Set relative mouse mode for a window.
385
 *
386
 * While the window has focus and relative mouse mode is enabled, the cursor
387
 * is hidden, the mouse position is constrained to the window, and SDL will
388
 * report continuous relative mouse motion even if the mouse is at the edge of
389
 * the window.
390
 *
391
 * If you'd like to keep the mouse position fixed while in relative mode you
392
 * can use SDL_SetWindowMouseRect(). If you'd like the cursor to be at a
393
 * specific location when relative mode ends, you should use
394
 * SDL_WarpMouseInWindow() before disabling relative mode.
395
 *
396
 * This function will flush any pending mouse motion for this window.
397
 *
398
 * \param window the window to change.
399
 * \param enabled true to enable relative mode, false to disable.
400
 * \returns true on success or false on failure; call SDL_GetError() for more
401
 *          information.
402
 *
403
 * \threadsafety This function should only be called on the main thread.
404
 *
405
 * \since This function is available since SDL 3.2.0.
406
 *
407
 * \sa SDL_GetWindowRelativeMouseMode
408
 */
409
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled);
410
411
/**
412
 * Query whether relative mouse mode is enabled for a window.
413
 *
414
 * \param window the window to query.
415
 * \returns true if relative mode is enabled for a window or false otherwise.
416
 *
417
 * \threadsafety This function should only be called on the main thread.
418
 *
419
 * \since This function is available since SDL 3.2.0.
420
 *
421
 * \sa SDL_SetWindowRelativeMouseMode
422
 */
423
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowRelativeMouseMode(SDL_Window *window);
424
425
/**
426
 * Capture the mouse and to track input outside an SDL window.
427
 *
428
 * Capturing enables your app to obtain mouse events globally, instead of just
429
 * within your window. Not all video targets support this function. When
430
 * capturing is enabled, the current window will get all mouse events, but
431
 * unlike relative mode, no change is made to the cursor and it is not
432
 * restrained to your window.
433
 *
434
 * This function may also deny mouse input to other windows--both those in
435
 * your application and others on the system--so you should use this function
436
 * sparingly, and in small bursts. For example, you might want to track the
437
 * mouse while the user is dragging something, until the user releases a mouse
438
 * button. It is not recommended that you capture the mouse for long periods
439
 * of time, such as the entire time your app is running. For that, you should
440
 * probably use SDL_SetWindowRelativeMouseMode() or SDL_SetWindowMouseGrab(),
441
 * depending on your goals.
442
 *
443
 * While captured, mouse events still report coordinates relative to the
444
 * current (foreground) window, but those coordinates may be outside the
445
 * bounds of the window (including negative values). Capturing is only allowed
446
 * for the foreground window. If the window loses focus while capturing, the
447
 * capture will be disabled automatically.
448
 *
449
 * While capturing is enabled, the current window will have the
450
 * `SDL_WINDOW_MOUSE_CAPTURE` flag set.
451
 *
452
 * Please note that SDL will attempt to "auto capture" the mouse while the
453
 * user is pressing a button; this is to try and make mouse behavior more
454
 * consistent between platforms, and deal with the common case of a user
455
 * dragging the mouse outside of the window. This means that if you are
456
 * calling SDL_CaptureMouse() only to deal with this situation, you do not
457
 * have to (although it is safe to do so). If this causes problems for your
458
 * app, you can disable auto capture by setting the
459
 * `SDL_HINT_MOUSE_AUTO_CAPTURE` hint to zero.
460
 *
461
 * \param enabled true to enable capturing, false to disable.
462
 * \returns true on success or false on failure; call SDL_GetError() for more
463
 *          information.
464
 *
465
 * \threadsafety This function should only be called on the main thread.
466
 *
467
 * \since This function is available since SDL 3.2.0.
468
 *
469
 * \sa SDL_GetGlobalMouseState
470
 */
471
extern SDL_DECLSPEC bool SDLCALL SDL_CaptureMouse(bool enabled);
472
473
/**
474
 * Create a cursor using the specified bitmap data and mask (in MSB format).
475
 *
476
 * `mask` has to be in MSB (Most Significant Bit) format.
477
 *
478
 * The cursor width (`w`) must be a multiple of 8 bits.
479
 *
480
 * The cursor is created in black and white according to the following:
481
 *
482
 * - data=0, mask=1: white
483
 * - data=1, mask=1: black
484
 * - data=0, mask=0: transparent
485
 * - data=1, mask=0: inverted color if possible, black if not.
486
 *
487
 * Cursors created with this function must be freed with SDL_DestroyCursor().
488
 *
489
 * If you want to have a color cursor, or create your cursor from an
490
 * SDL_Surface, you should use SDL_CreateColorCursor(). Alternately, you can
491
 * hide the cursor and draw your own as part of your game's rendering, but it
492
 * will be bound to the framerate.
493
 *
494
 * Also, SDL_CreateSystemCursor() is available, which provides several
495
 * readily-available system cursors to pick from.
496
 *
497
 * \param data the color value for each pixel of the cursor.
498
 * \param mask the mask value for each pixel of the cursor.
499
 * \param w the width of the cursor.
500
 * \param h the height of the cursor.
501
 * \param hot_x the x-axis offset from the left of the cursor image to the
502
 *              mouse x position, in the range of 0 to `w` - 1.
503
 * \param hot_y the y-axis offset from the top of the cursor image to the
504
 *              mouse y position, in the range of 0 to `h` - 1.
505
 * \returns a new cursor with the specified parameters on success or NULL on
506
 *          failure; call SDL_GetError() for more information.
507
 *
508
 * \threadsafety This function should only be called on the main thread.
509
 *
510
 * \since This function is available since SDL 3.2.0.
511
 *
512
 * \sa SDL_CreateColorCursor
513
 * \sa SDL_CreateSystemCursor
514
 * \sa SDL_DestroyCursor
515
 * \sa SDL_SetCursor
516
 */
517
extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateCursor(const Uint8 *data,
518
                                                     const Uint8 *mask,
519
                                                     int w, int h, int hot_x,
520
                                                     int hot_y);
521
522
/**
523
 * Create a color cursor.
524
 *
525
 * If this function is passed a surface with alternate representations, the
526
 * surface will be interpreted as the content to be used for 100% display
527
 * scale, and the alternate representations will be used for high DPI
528
 * situations. For example, if the original surface is 32x32, then on a 2x
529
 * macOS display or 200% display scale on Windows, a 64x64 version of the
530
 * image will be used, if available. If a matching version of the image isn't
531
 * available, the closest larger size image will be downscaled to the
532
 * appropriate size and be used instead, if available. Otherwise, the closest
533
 * smaller image will be upscaled and be used instead.
534
 *
535
 * \param surface an SDL_Surface structure representing the cursor image.
536
 * \param hot_x the x position of the cursor hot spot.
537
 * \param hot_y the y position of the cursor hot spot.
538
 * \returns the new cursor on success or NULL on failure; call SDL_GetError()
539
 *          for more information.
540
 *
541
 * \threadsafety This function should only be called on the main thread.
542
 *
543
 * \since This function is available since SDL 3.2.0.
544
 *
545
 * \sa SDL_CreateCursor
546
 * \sa SDL_CreateSystemCursor
547
 * \sa SDL_DestroyCursor
548
 * \sa SDL_SetCursor
549
 */
550
extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateColorCursor(SDL_Surface *surface,
551
                                                          int hot_x,
552
                                                          int hot_y);
553
554
/**
555
 * Create a system cursor.
556
 *
557
 * \param id an SDL_SystemCursor enum value.
558
 * \returns a cursor on success or NULL on failure; call SDL_GetError() for
559
 *          more information.
560
 *
561
 * \threadsafety This function should only be called on the main thread.
562
 *
563
 * \since This function is available since SDL 3.2.0.
564
 *
565
 * \sa SDL_DestroyCursor
566
 */
567
extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateSystemCursor(SDL_SystemCursor id);
568
569
/**
570
 * Set the active cursor.
571
 *
572
 * This function sets the currently active cursor to the specified one. If the
573
 * cursor is currently visible, the change will be immediately represented on
574
 * the display. SDL_SetCursor(NULL) can be used to force cursor redraw, if
575
 * this is desired for any reason.
576
 *
577
 * \param cursor a cursor to make active.
578
 * \returns true on success or false on failure; call SDL_GetError() for more
579
 *          information.
580
 *
581
 * \threadsafety This function should only be called on the main thread.
582
 *
583
 * \since This function is available since SDL 3.2.0.
584
 *
585
 * \sa SDL_GetCursor
586
 */
587
extern SDL_DECLSPEC bool SDLCALL SDL_SetCursor(SDL_Cursor *cursor);
588
589
/**
590
 * Get the active cursor.
591
 *
592
 * This function returns a pointer to the current cursor which is owned by the
593
 * library. It is not necessary to free the cursor with SDL_DestroyCursor().
594
 *
595
 * \returns the active cursor or NULL if there is no mouse.
596
 *
597
 * \threadsafety This function should only be called on the main thread.
598
 *
599
 * \since This function is available since SDL 3.2.0.
600
 *
601
 * \sa SDL_SetCursor
602
 */
603
extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_GetCursor(void);
604
605
/**
606
 * Get the default cursor.
607
 *
608
 * You do not have to call SDL_DestroyCursor() on the return value, but it is
609
 * safe to do so.
610
 *
611
 * \returns the default cursor on success or NULL on failuree; call
612
 *          SDL_GetError() for more information.
613
 *
614
 * \threadsafety This function should only be called on the main thread.
615
 *
616
 * \since This function is available since SDL 3.2.0.
617
 */
618
extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_GetDefaultCursor(void);
619
620
/**
621
 * Free a previously-created cursor.
622
 *
623
 * Use this function to free cursor resources created with SDL_CreateCursor(),
624
 * SDL_CreateColorCursor() or SDL_CreateSystemCursor().
625
 *
626
 * \param cursor the cursor to free.
627
 *
628
 * \threadsafety This function should only be called on the main thread.
629
 *
630
 * \since This function is available since SDL 3.2.0.
631
 *
632
 * \sa SDL_CreateColorCursor
633
 * \sa SDL_CreateCursor
634
 * \sa SDL_CreateSystemCursor
635
 */
636
extern SDL_DECLSPEC void SDLCALL SDL_DestroyCursor(SDL_Cursor *cursor);
637
638
/**
639
 * Show the cursor.
640
 *
641
 * \returns true on success or false on failure; call SDL_GetError() for more
642
 *          information.
643
 *
644
 * \threadsafety This function should only be called on the main thread.
645
 *
646
 * \since This function is available since SDL 3.2.0.
647
 *
648
 * \sa SDL_CursorVisible
649
 * \sa SDL_HideCursor
650
 */
651
extern SDL_DECLSPEC bool SDLCALL SDL_ShowCursor(void);
652
653
/**
654
 * Hide the cursor.
655
 *
656
 * \returns true on success or false on failure; call SDL_GetError() for more
657
 *          information.
658
 *
659
 * \threadsafety This function should only be called on the main thread.
660
 *
661
 * \since This function is available since SDL 3.2.0.
662
 *
663
 * \sa SDL_CursorVisible
664
 * \sa SDL_ShowCursor
665
 */
666
extern SDL_DECLSPEC bool SDLCALL SDL_HideCursor(void);
667
668
/**
669
 * Return whether the cursor is currently being shown.
670
 *
671
 * \returns `true` if the cursor is being shown, or `false` if the cursor is
672
 *          hidden.
673
 *
674
 * \threadsafety This function should only be called on the main thread.
675
 *
676
 * \since This function is available since SDL 3.2.0.
677
 *
678
 * \sa SDL_HideCursor
679
 * \sa SDL_ShowCursor
680
 */
681
extern SDL_DECLSPEC bool SDLCALL SDL_CursorVisible(void);
682
683
/* Ends C function definitions when using C++ */
684
#ifdef __cplusplus
685
}
686
#endif
687
#include <SDL3/SDL_close_code.h>
688
689
#endif /* SDL_mouse_h_ */
/opt/homebrew/include/SDL3/SDL_rect.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryRect
24
 *
25
 * Some helper functions for managing rectangles and 2D points, in both
26
 * integer and floating point versions.
27
 */
28
29
#ifndef SDL_rect_h_
30
#define SDL_rect_h_
31
32
#include <SDL3/SDL_stdinc.h>
33
#include <SDL3/SDL_error.h>
34
35
#include <SDL3/SDL_begin_code.h>
36
/* Set up for C function definitions, even when using C++ */
37
#ifdef __cplusplus
38
extern "C" {
39
#endif
40
41
/**
42
 * The structure that defines a point (using integers).
43
 *
44
 * \since This struct is available since SDL 3.2.0.
45
 *
46
 * \sa SDL_GetRectEnclosingPoints
47
 * \sa SDL_PointInRect
48
 */
49
typedef struct SDL_Point
50
{
51
    int x;
52
    int y;
53
} SDL_Point;
54
55
/**
56
 * The structure that defines a point (using floating point values).
57
 *
58
 * \since This struct is available since SDL 3.2.0.
59
 *
60
 * \sa SDL_GetRectEnclosingPointsFloat
61
 * \sa SDL_PointInRectFloat
62
 */
63
typedef struct SDL_FPoint
64
{
65
    float x;
66
    float y;
67
} SDL_FPoint;
68
69
70
/**
71
 * A rectangle, with the origin at the upper left (using integers).
72
 *
73
 * \since This struct is available since SDL 3.2.0.
74
 *
75
 * \sa SDL_RectEmpty
76
 * \sa SDL_RectsEqual
77
 * \sa SDL_HasRectIntersection
78
 * \sa SDL_GetRectIntersection
79
 * \sa SDL_GetRectAndLineIntersection
80
 * \sa SDL_GetRectUnion
81
 * \sa SDL_GetRectEnclosingPoints
82
 */
83
typedef struct SDL_Rect
84
{
85
    int x, y;
86
    int w, h;
87
} SDL_Rect;
88
89
90
/**
91
 * A rectangle, with the origin at the upper left (using floating point
92
 * values).
93
 *
94
 * \since This struct is available since SDL 3.2.0.
95
 *
96
 * \sa SDL_RectEmptyFloat
97
 * \sa SDL_RectsEqualFloat
98
 * \sa SDL_RectsEqualEpsilon
99
 * \sa SDL_HasRectIntersectionFloat
100
 * \sa SDL_GetRectIntersectionFloat
101
 * \sa SDL_GetRectAndLineIntersectionFloat
102
 * \sa SDL_GetRectUnionFloat
103
 * \sa SDL_GetRectEnclosingPointsFloat
104
 * \sa SDL_PointInRectFloat
105
 */
106
typedef struct SDL_FRect
107
{
108
    float x;
109
    float y;
110
    float w;
111
    float h;
112
} SDL_FRect;
113
114
115
/**
116
 * Convert an SDL_Rect to SDL_FRect
117
 *
118
 * \param rect a pointer to an SDL_Rect.
119
 * \param frect a pointer filled in with the floating point representation of
120
 *              `rect`.
121
 *
122
 * \threadsafety It is safe to call this function from any thread.
123
 *
124
 * \since This function is available since SDL 3.2.0.
125
 */
126
SDL_FORCE_INLINE void SDL_RectToFRect(const SDL_Rect *rect, SDL_FRect *frect)
127
0
{
128
0
    frect->x = (float)rect->x;
129
0
    frect->y = (float)rect->y;
130
0
    frect->w = (float)rect->w;
131
0
    frect->h = (float)rect->h;
132
0
}
Unexecuted instantiation: emulator.cpp:_ZL15SDL_RectToFRectPK8SDL_RectP9SDL_FRect
Unexecuted instantiation: sound.cpp:_ZL15SDL_RectToFRectPK8SDL_RectP9SDL_FRect
Unexecuted instantiation: ay8912.cpp:_ZL15SDL_RectToFRectPK8SDL_RectP9SDL_FRect
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL15SDL_RectToFRectPK8SDL_RectP9SDL_FRect
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL15SDL_RectToFRectPK8SDL_RectP9SDL_FRect
133
134
/**
135
 * Determine whether a point resides inside a rectangle.
136
 *
137
 * A point is considered part of a rectangle if both `p` and `r` are not NULL,
138
 * and `p`'s x and y coordinates are >= to the rectangle's top left corner,
139
 * and < the rectangle's x+w and y+h. So a 1x1 rectangle considers point (0,0)
140
 * as "inside" and (0,1) as not.
141
 *
142
 * Note that this is a forced-inline function in a header, and not a public
143
 * API function available in the SDL library (which is to say, the code is
144
 * embedded in the calling program and the linker and dynamic loader will not
145
 * be able to find this function inside SDL itself).
146
 *
147
 * \param p the point to test.
148
 * \param r the rectangle to test.
149
 * \returns true if `p` is contained by `r`, false otherwise.
150
 *
151
 * \threadsafety It is safe to call this function from any thread.
152
 *
153
 * \since This function is available since SDL 3.2.0.
154
 */
155
SDL_FORCE_INLINE bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
156
0
{
157
0
    return ( p && r && (p->x >= r->x) && (p->x < (r->x + r->w)) &&
158
0
             (p->y >= r->y) && (p->y < (r->y + r->h)) ) ? true : false;
159
0
}
Unexecuted instantiation: emulator.cpp:_ZL15SDL_PointInRectPK9SDL_PointPK8SDL_Rect
Unexecuted instantiation: sound.cpp:_ZL15SDL_PointInRectPK9SDL_PointPK8SDL_Rect
Unexecuted instantiation: ay8912.cpp:_ZL15SDL_PointInRectPK9SDL_PointPK8SDL_Rect
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL15SDL_PointInRectPK9SDL_PointPK8SDL_Rect
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL15SDL_PointInRectPK9SDL_PointPK8SDL_Rect
160
161
/**
162
 * Determine whether a rectangle has no area.
163
 *
164
 * A rectangle is considered "empty" for this function if `r` is NULL, or if
165
 * `r`'s width and/or height are <= 0.
166
 *
167
 * Note that this is a forced-inline function in a header, and not a public
168
 * API function available in the SDL library (which is to say, the code is
169
 * embedded in the calling program and the linker and dynamic loader will not
170
 * be able to find this function inside SDL itself).
171
 *
172
 * \param r the rectangle to test.
173
 * \returns true if the rectangle is "empty", false otherwise.
174
 *
175
 * \threadsafety It is safe to call this function from any thread.
176
 *
177
 * \since This function is available since SDL 3.2.0.
178
 */
179
SDL_FORCE_INLINE bool SDL_RectEmpty(const SDL_Rect *r)
180
0
{
181
0
    return ((!r) || (r->w <= 0) || (r->h <= 0)) ? true : false;
182
0
}
Unexecuted instantiation: emulator.cpp:_ZL13SDL_RectEmptyPK8SDL_Rect
Unexecuted instantiation: sound.cpp:_ZL13SDL_RectEmptyPK8SDL_Rect
Unexecuted instantiation: ay8912.cpp:_ZL13SDL_RectEmptyPK8SDL_Rect
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL13SDL_RectEmptyPK8SDL_Rect
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL13SDL_RectEmptyPK8SDL_Rect
183
184
/**
185
 * Determine whether two rectangles are equal.
186
 *
187
 * Rectangles are considered equal if both are not NULL and each of their x,
188
 * y, width and height match.
189
 *
190
 * Note that this is a forced-inline function in a header, and not a public
191
 * API function available in the SDL library (which is to say, the code is
192
 * embedded in the calling program and the linker and dynamic loader will not
193
 * be able to find this function inside SDL itself).
194
 *
195
 * \param a the first rectangle to test.
196
 * \param b the second rectangle to test.
197
 * \returns true if the rectangles are equal, false otherwise.
198
 *
199
 * \threadsafety It is safe to call this function from any thread.
200
 *
201
 * \since This function is available since SDL 3.2.0.
202
 */
203
SDL_FORCE_INLINE bool SDL_RectsEqual(const SDL_Rect *a, const SDL_Rect *b)
204
0
{
205
0
    return (a && b && (a->x == b->x) && (a->y == b->y) &&
206
0
            (a->w == b->w) && (a->h == b->h)) ? true : false;
207
0
}
Unexecuted instantiation: emulator.cpp:_ZL14SDL_RectsEqualPK8SDL_RectS1_
Unexecuted instantiation: sound.cpp:_ZL14SDL_RectsEqualPK8SDL_RectS1_
Unexecuted instantiation: ay8912.cpp:_ZL14SDL_RectsEqualPK8SDL_RectS1_
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL14SDL_RectsEqualPK8SDL_RectS1_
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL14SDL_RectsEqualPK8SDL_RectS1_
208
209
/**
210
 * Determine whether two rectangles intersect.
211
 *
212
 * If either pointer is NULL the function will return false.
213
 *
214
 * \param A an SDL_Rect structure representing the first rectangle.
215
 * \param B an SDL_Rect structure representing the second rectangle.
216
 * \returns true if there is an intersection, false otherwise.
217
 *
218
 * \threadsafety It is safe to call this function from any thread.
219
 *
220
 * \since This function is available since SDL 3.2.0.
221
 *
222
 * \sa SDL_GetRectIntersection
223
 */
224
extern SDL_DECLSPEC bool SDLCALL SDL_HasRectIntersection(const SDL_Rect *A, const SDL_Rect *B);
225
226
/**
227
 * Calculate the intersection of two rectangles.
228
 *
229
 * If `result` is NULL then this function will return false.
230
 *
231
 * \param A an SDL_Rect structure representing the first rectangle.
232
 * \param B an SDL_Rect structure representing the second rectangle.
233
 * \param result an SDL_Rect structure filled in with the intersection of
234
 *               rectangles `A` and `B`.
235
 * \returns true if there is an intersection, false otherwise.
236
 *
237
 * \since This function is available since SDL 3.2.0.
238
 *
239
 * \sa SDL_HasRectIntersection
240
 */
241
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectIntersection(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result);
242
243
/**
244
 * Calculate the union of two rectangles.
245
 *
246
 * \param A an SDL_Rect structure representing the first rectangle.
247
 * \param B an SDL_Rect structure representing the second rectangle.
248
 * \param result an SDL_Rect structure filled in with the union of rectangles
249
 *               `A` and `B`.
250
 * \returns true on success or false on failure; call SDL_GetError() for more
251
 *          information.
252
 *
253
 * \since This function is available since SDL 3.2.0.
254
 */
255
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectUnion(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result);
256
257
/**
258
 * Calculate a minimal rectangle enclosing a set of points.
259
 *
260
 * If `clip` is not NULL then only points inside of the clipping rectangle are
261
 * considered.
262
 *
263
 * \param points an array of SDL_Point structures representing points to be
264
 *               enclosed.
265
 * \param count the number of structures in the `points` array.
266
 * \param clip an SDL_Rect used for clipping or NULL to enclose all points.
267
 * \param result an SDL_Rect structure filled in with the minimal enclosing
268
 *               rectangle.
269
 * \returns true if any points were enclosed or false if all the points were
270
 *          outside of the clipping rectangle.
271
 *
272
 * \since This function is available since SDL 3.2.0.
273
 */
274
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectEnclosingPoints(const SDL_Point *points, int count, const SDL_Rect *clip, SDL_Rect *result);
275
276
/**
277
 * Calculate the intersection of a rectangle and line segment.
278
 *
279
 * This function is used to clip a line segment to a rectangle. A line segment
280
 * contained entirely within the rectangle or that does not intersect will
281
 * remain unchanged. A line segment that crosses the rectangle at either or
282
 * both ends will be clipped to the boundary of the rectangle and the new
283
 * coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary.
284
 *
285
 * \param rect an SDL_Rect structure representing the rectangle to intersect.
286
 * \param X1 a pointer to the starting X-coordinate of the line.
287
 * \param Y1 a pointer to the starting Y-coordinate of the line.
288
 * \param X2 a pointer to the ending X-coordinate of the line.
289
 * \param Y2 a pointer to the ending Y-coordinate of the line.
290
 * \returns true if there is an intersection, false otherwise.
291
 *
292
 * \since This function is available since SDL 3.2.0.
293
 */
294
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectAndLineIntersection(const SDL_Rect *rect, int *X1, int *Y1, int *X2, int *Y2);
295
296
297
/* SDL_FRect versions... */
298
299
/**
300
 * Determine whether a point resides inside a floating point rectangle.
301
 *
302
 * A point is considered part of a rectangle if both `p` and `r` are not NULL,
303
 * and `p`'s x and y coordinates are >= to the rectangle's top left corner,
304
 * and <= the rectangle's x+w and y+h. So a 1x1 rectangle considers point
305
 * (0,0) and (0,1) as "inside" and (0,2) as not.
306
 *
307
 * Note that this is a forced-inline function in a header, and not a public
308
 * API function available in the SDL library (which is to say, the code is
309
 * embedded in the calling program and the linker and dynamic loader will not
310
 * be able to find this function inside SDL itself).
311
 *
312
 * \param p the point to test.
313
 * \param r the rectangle to test.
314
 * \returns true if `p` is contained by `r`, false otherwise.
315
 *
316
 * \threadsafety It is safe to call this function from any thread.
317
 *
318
 * \since This function is available since SDL 3.2.0.
319
 */
320
SDL_FORCE_INLINE bool SDL_PointInRectFloat(const SDL_FPoint *p, const SDL_FRect *r)
321
0
{
322
0
    return ( p && r && (p->x >= r->x) && (p->x <= (r->x + r->w)) &&
323
0
             (p->y >= r->y) && (p->y <= (r->y + r->h)) ) ? true : false;
324
0
}
Unexecuted instantiation: emulator.cpp:_ZL20SDL_PointInRectFloatPK10SDL_FPointPK9SDL_FRect
Unexecuted instantiation: sound.cpp:_ZL20SDL_PointInRectFloatPK10SDL_FPointPK9SDL_FRect
Unexecuted instantiation: ay8912.cpp:_ZL20SDL_PointInRectFloatPK10SDL_FPointPK9SDL_FRect
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL20SDL_PointInRectFloatPK10SDL_FPointPK9SDL_FRect
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL20SDL_PointInRectFloatPK10SDL_FPointPK9SDL_FRect
325
326
/**
327
 * Determine whether a floating point rectangle can contain any point.
328
 *
329
 * A rectangle is considered "empty" for this function if `r` is NULL, or if
330
 * `r`'s width and/or height are < 0.0f.
331
 *
332
 * Note that this is a forced-inline function in a header, and not a public
333
 * API function available in the SDL library (which is to say, the code is
334
 * embedded in the calling program and the linker and dynamic loader will not
335
 * be able to find this function inside SDL itself).
336
 *
337
 * \param r the rectangle to test.
338
 * \returns true if the rectangle is "empty", false otherwise.
339
 *
340
 * \threadsafety It is safe to call this function from any thread.
341
 *
342
 * \since This function is available since SDL 3.2.0.
343
 */
344
SDL_FORCE_INLINE bool SDL_RectEmptyFloat(const SDL_FRect *r)
345
0
{
346
0
    return ((!r) || (r->w < 0.0f) || (r->h < 0.0f)) ? true : false;
347
0
}
Unexecuted instantiation: emulator.cpp:_ZL18SDL_RectEmptyFloatPK9SDL_FRect
Unexecuted instantiation: sound.cpp:_ZL18SDL_RectEmptyFloatPK9SDL_FRect
Unexecuted instantiation: ay8912.cpp:_ZL18SDL_RectEmptyFloatPK9SDL_FRect
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL18SDL_RectEmptyFloatPK9SDL_FRect
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL18SDL_RectEmptyFloatPK9SDL_FRect
348
349
/**
350
 * Determine whether two floating point rectangles are equal, within some
351
 * given epsilon.
352
 *
353
 * Rectangles are considered equal if both are not NULL and each of their x,
354
 * y, width and height are within `epsilon` of each other. If you don't know
355
 * what value to use for `epsilon`, you should call the SDL_RectsEqualFloat
356
 * function instead.
357
 *
358
 * Note that this is a forced-inline function in a header, and not a public
359
 * API function available in the SDL library (which is to say, the code is
360
 * embedded in the calling program and the linker and dynamic loader will not
361
 * be able to find this function inside SDL itself).
362
 *
363
 * \param a the first rectangle to test.
364
 * \param b the second rectangle to test.
365
 * \param epsilon the epsilon value for comparison.
366
 * \returns true if the rectangles are equal, false otherwise.
367
 *
368
 * \threadsafety It is safe to call this function from any thread.
369
 *
370
 * \since This function is available since SDL 3.2.0.
371
 *
372
 * \sa SDL_RectsEqualFloat
373
 */
374
SDL_FORCE_INLINE bool SDL_RectsEqualEpsilon(const SDL_FRect *a, const SDL_FRect *b, float epsilon)
375
0
{
376
0
    return (a && b && ((a == b) ||
377
0
            ((SDL_fabsf(a->x - b->x) <= epsilon) &&
378
0
            (SDL_fabsf(a->y - b->y) <= epsilon) &&
379
0
            (SDL_fabsf(a->w - b->w) <= epsilon) &&
380
0
            (SDL_fabsf(a->h - b->h) <= epsilon))))
381
0
            ? true : false;
382
0
}
Unexecuted instantiation: emulator.cpp:_ZL21SDL_RectsEqualEpsilonPK9SDL_FRectS1_f
Unexecuted instantiation: sound.cpp:_ZL21SDL_RectsEqualEpsilonPK9SDL_FRectS1_f
Unexecuted instantiation: ay8912.cpp:_ZL21SDL_RectsEqualEpsilonPK9SDL_FRectS1_f
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL21SDL_RectsEqualEpsilonPK9SDL_FRectS1_f
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL21SDL_RectsEqualEpsilonPK9SDL_FRectS1_f
383
384
/**
385
 * Determine whether two floating point rectangles are equal, within a default
386
 * epsilon.
387
 *
388
 * Rectangles are considered equal if both are not NULL and each of their x,
389
 * y, width and height are within SDL_FLT_EPSILON of each other. This is often
390
 * a reasonable way to compare two floating point rectangles and deal with the
391
 * slight precision variations in floating point calculations that tend to pop
392
 * up.
393
 *
394
 * Note that this is a forced-inline function in a header, and not a public
395
 * API function available in the SDL library (which is to say, the code is
396
 * embedded in the calling program and the linker and dynamic loader will not
397
 * be able to find this function inside SDL itself).
398
 *
399
 * \param a the first rectangle to test.
400
 * \param b the second rectangle to test.
401
 * \returns true if the rectangles are equal, false otherwise.
402
 *
403
 * \threadsafety It is safe to call this function from any thread.
404
 *
405
 * \since This function is available since SDL 3.2.0.
406
 *
407
 * \sa SDL_RectsEqualEpsilon
408
 */
409
SDL_FORCE_INLINE bool SDL_RectsEqualFloat(const SDL_FRect *a, const SDL_FRect *b)
410
0
{
411
0
    return SDL_RectsEqualEpsilon(a, b, SDL_FLT_EPSILON);
412
0
}
Unexecuted instantiation: emulator.cpp:_ZL19SDL_RectsEqualFloatPK9SDL_FRectS1_
Unexecuted instantiation: sound.cpp:_ZL19SDL_RectsEqualFloatPK9SDL_FRectS1_
Unexecuted instantiation: ay8912.cpp:_ZL19SDL_RectsEqualFloatPK9SDL_FRectS1_
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL19SDL_RectsEqualFloatPK9SDL_FRectS1_
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL19SDL_RectsEqualFloatPK9SDL_FRectS1_
413
414
/**
415
 * Determine whether two rectangles intersect with float precision.
416
 *
417
 * If either pointer is NULL the function will return false.
418
 *
419
 * \param A an SDL_FRect structure representing the first rectangle.
420
 * \param B an SDL_FRect structure representing the second rectangle.
421
 * \returns true if there is an intersection, false otherwise.
422
 *
423
 * \since This function is available since SDL 3.2.0.
424
 *
425
 * \sa SDL_GetRectIntersection
426
 */
427
extern SDL_DECLSPEC bool SDLCALL SDL_HasRectIntersectionFloat(const SDL_FRect *A, const SDL_FRect *B);
428
429
/**
430
 * Calculate the intersection of two rectangles with float precision.
431
 *
432
 * If `result` is NULL then this function will return false.
433
 *
434
 * \param A an SDL_FRect structure representing the first rectangle.
435
 * \param B an SDL_FRect structure representing the second rectangle.
436
 * \param result an SDL_FRect structure filled in with the intersection of
437
 *               rectangles `A` and `B`.
438
 * \returns true if there is an intersection, false otherwise.
439
 *
440
 * \since This function is available since SDL 3.2.0.
441
 *
442
 * \sa SDL_HasRectIntersectionFloat
443
 */
444
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectIntersectionFloat(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result);
445
446
/**
447
 * Calculate the union of two rectangles with float precision.
448
 *
449
 * \param A an SDL_FRect structure representing the first rectangle.
450
 * \param B an SDL_FRect structure representing the second rectangle.
451
 * \param result an SDL_FRect structure filled in with the union of rectangles
452
 *               `A` and `B`.
453
 * \returns true on success or false on failure; call SDL_GetError() for more
454
 *          information.
455
 *
456
 * \since This function is available since SDL 3.2.0.
457
 */
458
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectUnionFloat(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result);
459
460
/**
461
 * Calculate a minimal rectangle enclosing a set of points with float
462
 * precision.
463
 *
464
 * If `clip` is not NULL then only points inside of the clipping rectangle are
465
 * considered.
466
 *
467
 * \param points an array of SDL_FPoint structures representing points to be
468
 *               enclosed.
469
 * \param count the number of structures in the `points` array.
470
 * \param clip an SDL_FRect used for clipping or NULL to enclose all points.
471
 * \param result an SDL_FRect structure filled in with the minimal enclosing
472
 *               rectangle.
473
 * \returns true if any points were enclosed or false if all the points were
474
 *          outside of the clipping rectangle.
475
 *
476
 * \since This function is available since SDL 3.2.0.
477
 */
478
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectEnclosingPointsFloat(const SDL_FPoint *points, int count, const SDL_FRect *clip, SDL_FRect *result);
479
480
/**
481
 * Calculate the intersection of a rectangle and line segment with float
482
 * precision.
483
 *
484
 * This function is used to clip a line segment to a rectangle. A line segment
485
 * contained entirely within the rectangle or that does not intersect will
486
 * remain unchanged. A line segment that crosses the rectangle at either or
487
 * both ends will be clipped to the boundary of the rectangle and the new
488
 * coordinates saved in `X1`, `Y1`, `X2`, and/or `Y2` as necessary.
489
 *
490
 * \param rect an SDL_FRect structure representing the rectangle to intersect.
491
 * \param X1 a pointer to the starting X-coordinate of the line.
492
 * \param Y1 a pointer to the starting Y-coordinate of the line.
493
 * \param X2 a pointer to the ending X-coordinate of the line.
494
 * \param Y2 a pointer to the ending Y-coordinate of the line.
495
 * \returns true if there is an intersection, false otherwise.
496
 *
497
 * \since This function is available since SDL 3.2.0.
498
 */
499
extern SDL_DECLSPEC bool SDLCALL SDL_GetRectAndLineIntersectionFloat(const SDL_FRect *rect, float *X1, float *Y1, float *X2, float *Y2);
500
501
/* Ends C function definitions when using C++ */
502
#ifdef __cplusplus
503
}
504
#endif
505
#include <SDL3/SDL_close_code.h>
506
507
#endif /* SDL_rect_h_ */
/opt/homebrew/include/SDL3/SDL_stdinc.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryStdinc
24
 *
25
 * SDL provides its own implementation of some of the most important C runtime
26
 * functions.
27
 *
28
 * Using these functions allows an app to have access to common C
29
 * functionality without depending on a specific C runtime (or a C runtime at
30
 * all). More importantly, the SDL implementations work identically across
31
 * platforms, so apps can avoid surprises like snprintf() behaving differently
32
 * between Windows and Linux builds, or itoa() only existing on some
33
 * platforms.
34
 *
35
 * For many of the most common functions, like SDL_memcpy, SDL might just call
36
 * through to the usual C runtime behind the scenes, if it makes sense to do
37
 * so (if it's faster and always available/reliable on a given platform),
38
 * reducing library size and offering the most optimized option.
39
 *
40
 * SDL also offers other C-runtime-adjacent functionality in this header that
41
 * either isn't, strictly speaking, part of any C runtime standards, like
42
 * SDL_crc32() and SDL_reinterpret_cast, etc. It also offers a few better
43
 * options, like SDL_strlcpy(), which functions as a safer form of strcpy().
44
 */
45
46
#ifndef SDL_stdinc_h_
47
#define SDL_stdinc_h_
48
49
#include <SDL3/SDL_platform_defines.h>
50
51
#include <stdarg.h>
52
#include <stdint.h>
53
#include <string.h>
54
#include <wchar.h>
55
56
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \
57
    defined(SDL_INCLUDE_INTTYPES_H)
58
#include <inttypes.h>
59
#endif
60
61
#ifndef __cplusplus
62
#if defined(__has_include) && !defined(SDL_INCLUDE_STDBOOL_H)
63
#if __has_include(<stdbool.h>)
64
#define SDL_INCLUDE_STDBOOL_H
65
#endif
66
#endif
67
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \
68
    (defined(_MSC_VER) && (_MSC_VER >= 1910 /* Visual Studio 2017 */)) || \
69
    defined(SDL_INCLUDE_STDBOOL_H)
70
#include <stdbool.h>
71
#elif !defined(__bool_true_false_are_defined) && !defined(bool)
72
#define bool  unsigned char
73
#define false 0
74
#define true  1
75
#define __bool_true_false_are_defined 1
76
#endif
77
#endif /* !__cplusplus */
78
79
#ifndef SDL_DISABLE_ALLOCA
80
# ifndef alloca
81
#  ifdef HAVE_ALLOCA_H
82
#   include <alloca.h>
83
#  elif defined(SDL_PLATFORM_NETBSD)
84
#   if defined(__STRICT_ANSI__)
85
#    define SDL_DISABLE_ALLOCA
86
#   else
87
#    include <stdlib.h>
88
#   endif
89
#  elif defined(__GNUC__)
90
#   define alloca __builtin_alloca
91
#  elif defined(_MSC_VER)
92
#   include <malloc.h>
93
#   define alloca _alloca
94
#  elif defined(__WATCOMC__)
95
#   include <malloc.h>
96
#  elif defined(__BORLANDC__)
97
#   include <malloc.h>
98
#  elif defined(__DMC__)
99
#   include <stdlib.h>
100
#  elif defined(SDL_PLATFORM_AIX)
101
# pragma alloca
102
#  elif defined(__MRC__)
103
void *alloca(unsigned);
104
#  else
105
void *alloca(size_t);
106
#  endif
107
# endif
108
#endif
109
110
111
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
112
113
/**
114
 * Don't let SDL use "long long" C types.
115
 *
116
 * SDL will define this if it believes the compiler doesn't understand the
117
 * "long long" syntax for C datatypes. This can happen on older compilers.
118
 *
119
 * If _your_ compiler doesn't support "long long" but SDL doesn't know it, it
120
 * is safe to define this yourself to build against the SDL headers.
121
 *
122
 * If this is defined, it will remove access to some C runtime support
123
 * functions, like SDL_ulltoa and SDL_strtoll that refer to this datatype
124
 * explicitly. The rest of SDL will still be available.
125
 *
126
 * SDL's own source code cannot be built with a compiler that has this
127
 * defined, for various technical reasons.
128
 */
129
#define SDL_NOLONGLONG 1
130
131
#elif defined(_MSC_VER) && (_MSC_VER < 1310)  /* long long introduced in Visual Studio.NET 2003 */
132
#  define SDL_NOLONGLONG 1
133
#endif
134
135
136
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
137
138
/**
139
 * The largest value that a `size_t` can hold for the target platform.
140
 *
141
 * `size_t` is generally the same size as a pointer in modern times, but this
142
 * can get weird on very old and very esoteric machines. For example, on a
143
 * 16-bit Intel 286, you might have a 32-bit "far" pointer (16-bit segment
144
 * plus 16-bit offset), but `size_t` is 16 bits, because it can only deal with
145
 * the offset into an individual segment.
146
 *
147
 * In modern times, it's generally expected to cover an entire linear address
148
 * space. But be careful!
149
 *
150
 * \since This macro is available since SDL 3.2.0.
151
 */
152
#define SDL_SIZE_MAX SIZE_MAX
153
154
#elif defined(SIZE_MAX)
155
# define SDL_SIZE_MAX SIZE_MAX
156
#else
157
# define SDL_SIZE_MAX ((size_t) -1)
158
#endif
159
160
#ifndef SDL_COMPILE_TIME_ASSERT
161
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
162
163
/**
164
 * A compile-time assertion.
165
 *
166
 * This can check constant values _known to the compiler at build time_ for
167
 * correctness, and end the compile with the error if they fail.
168
 *
169
 * Often times these are used to verify basic truths, like the size of a
170
 * datatype is what is expected:
171
 *
172
 * ```c
173
 * SDL_COMPILE_TIME_ASSERT(uint32_size, sizeof(Uint32) == 4);
174
 * ```
175
 *
176
 * The `name` parameter must be a valid C symbol, and must be unique across
177
 * all compile-time asserts in the same compilation unit (one run of the
178
 * compiler), or the build might fail with cryptic errors on some targets.
179
 * This is used with a C language trick that works on older compilers that
180
 * don't support better assertion techniques.
181
 *
182
 * If you need an assertion that operates at runtime, on variable data, you
183
 * should try SDL_assert instead.
184
 *
185
 * \param name a unique identifier for this assertion.
186
 * \param x the value to test. Must be a boolean value.
187
 *
188
 * \threadsafety This macro doesn't generate any code to run.
189
 *
190
 * \since This macro is available since SDL 3.2.0.
191
 *
192
 * \sa SDL_assert
193
 */
194
#define SDL_COMPILE_TIME_ASSERT(name, x) FailToCompileIf_x_IsFalse(x)
195
#elif defined(__cplusplus)
196
/* Keep C++ case alone: Some versions of gcc will define __STDC_VERSION__ even when compiling in C++ mode. */
197
#if (__cplusplus >= 201103L)
198
#define SDL_COMPILE_TIME_ASSERT(name, x)  static_assert(x, #x)
199
#endif
200
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 202311L)
201
#define SDL_COMPILE_TIME_ASSERT(name, x)  static_assert(x, #x)
202
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
203
#define SDL_COMPILE_TIME_ASSERT(name, x) _Static_assert(x, #x)
204
#endif
205
#endif /* !SDL_COMPILE_TIME_ASSERT */
206
207
#ifndef SDL_COMPILE_TIME_ASSERT
208
/* universal, but may trigger -Wunused-local-typedefs */
209
#define SDL_COMPILE_TIME_ASSERT(name, x)               \
210
       typedef int SDL_compile_time_assert_ ## name[(x) * 2 - 1]
211
#endif
212
213
/**
214
 * The number of elements in a static array.
215
 *
216
 * This will compile but return incorrect results for a pointer to an array;
217
 * it has to be an array the compiler knows the size of.
218
 *
219
 * This macro looks like it double-evaluates the argument, but it does so
220
 * inside of `sizeof`, so there are no side-effects here, as expressions do
221
 * not actually run any code in these cases.
222
 *
223
 * \since This macro is available since SDL 3.2.0.
224
 */
225
#define SDL_arraysize(array) (sizeof(array)/sizeof(array[0]))
226
227
/**
228
 * Macro useful for building other macros with strings in them.
229
 *
230
 * For example:
231
 *
232
 * ```c
233
 * #define LOG_ERROR(X) OutputDebugString(SDL_STRINGIFY_ARG(__FUNCTION__) ": " X "\n")`
234
 * ```
235
 *
236
 * \param arg the text to turn into a string literal.
237
 *
238
 * \since This macro is available since SDL 3.2.0.
239
 */
240
#define SDL_STRINGIFY_ARG(arg)  #arg
241
242
/**
243
 *  \name Cast operators
244
 *
245
 *  Use proper C++ casts when compiled as C++ to be compatible with the option
246
 *  -Wold-style-cast of GCC (and -Werror=old-style-cast in GCC 4.2 and above).
247
 */
248
/* @{ */
249
250
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
251
252
/**
253
 * Handle a Reinterpret Cast properly whether using C or C++.
254
 *
255
 * If compiled as C++, this macro offers a proper C++ reinterpret_cast<>.
256
 *
257
 * If compiled as C, this macro does a normal C-style cast.
258
 *
259
 * This is helpful to avoid compiler warnings in C++.
260
 *
261
 * \param type the type to cast the expression to.
262
 * \param expression the expression to cast to a different type.
263
 * \returns `expression`, cast to `type`.
264
 *
265
 * \threadsafety It is safe to call this macro from any thread.
266
 *
267
 * \since This macro is available since SDL 3.2.0.
268
 *
269
 * \sa SDL_static_cast
270
 * \sa SDL_const_cast
271
 */
272
#define SDL_reinterpret_cast(type, expression) reinterpret_cast<type>(expression)  /* or `((type)(expression))` in C */
273
274
/**
275
 * Handle a Static Cast properly whether using C or C++.
276
 *
277
 * If compiled as C++, this macro offers a proper C++ static_cast<>.
278
 *
279
 * If compiled as C, this macro does a normal C-style cast.
280
 *
281
 * This is helpful to avoid compiler warnings in C++.
282
 *
283
 * \param type the type to cast the expression to.
284
 * \param expression the expression to cast to a different type.
285
 * \returns `expression`, cast to `type`.
286
 *
287
 * \threadsafety It is safe to call this macro from any thread.
288
 *
289
 * \since This macro is available since SDL 3.2.0.
290
 *
291
 * \sa SDL_reinterpret_cast
292
 * \sa SDL_const_cast
293
 */
294
#define SDL_static_cast(type, expression) static_cast<type>(expression)  /* or `((type)(expression))` in C */
295
296
/**
297
 * Handle a Const Cast properly whether using C or C++.
298
 *
299
 * If compiled as C++, this macro offers a proper C++ const_cast<>.
300
 *
301
 * If compiled as C, this macro does a normal C-style cast.
302
 *
303
 * This is helpful to avoid compiler warnings in C++.
304
 *
305
 * \param type the type to cast the expression to.
306
 * \param expression the expression to cast to a different type.
307
 * \returns `expression`, cast to `type`.
308
 *
309
 * \threadsafety It is safe to call this macro from any thread.
310
 *
311
 * \since This macro is available since SDL 3.2.0.
312
 *
313
 * \sa SDL_reinterpret_cast
314
 * \sa SDL_static_cast
315
 */
316
#define SDL_const_cast(type, expression) const_cast<type>(expression)  /* or `((type)(expression))` in C */
317
318
#elif defined(__cplusplus)
319
#define SDL_reinterpret_cast(type, expression) reinterpret_cast<type>(expression)
320
#define SDL_static_cast(type, expression) static_cast<type>(expression)
321
#define SDL_const_cast(type, expression) const_cast<type>(expression)
322
#else
323
#define SDL_reinterpret_cast(type, expression) ((type)(expression))
324
#define SDL_static_cast(type, expression) ((type)(expression))
325
#define SDL_const_cast(type, expression) ((type)(expression))
326
#endif
327
328
/* @} *//* Cast operators */
329
330
/**
331
 * Define a four character code as a Uint32.
332
 *
333
 * \param A the first ASCII character.
334
 * \param B the second ASCII character.
335
 * \param C the third ASCII character.
336
 * \param D the fourth ASCII character.
337
 * \returns the four characters converted into a Uint32, one character
338
 *          per-byte.
339
 *
340
 * \threadsafety It is safe to call this macro from any thread.
341
 *
342
 * \since This macro is available since SDL 3.2.0.
343
 */
344
#define SDL_FOURCC(A, B, C, D) \
345
    ((SDL_static_cast(Uint32, SDL_static_cast(Uint8, (A))) << 0) | \
346
     (SDL_static_cast(Uint32, SDL_static_cast(Uint8, (B))) << 8) | \
347
     (SDL_static_cast(Uint32, SDL_static_cast(Uint8, (C))) << 16) | \
348
     (SDL_static_cast(Uint32, SDL_static_cast(Uint8, (D))) << 24))
349
350
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
351
352
/**
353
 * Append the 64 bit integer suffix to a signed integer literal.
354
 *
355
 * This helps compilers that might believe a integer literal larger than
356
 * 0xFFFFFFFF is overflowing a 32-bit value. Use `SDL_SINT64_C(0xFFFFFFFF1)`
357
 * instead of `0xFFFFFFFF1` by itself.
358
 *
359
 * \since This macro is available since SDL 3.2.0.
360
 *
361
 * \sa SDL_UINT64_C
362
 */
363
#define SDL_SINT64_C(c)  c ## LL  /* or whatever the current compiler uses. */
364
365
/**
366
 * Append the 64 bit integer suffix to an unsigned integer literal.
367
 *
368
 * This helps compilers that might believe a integer literal larger than
369
 * 0xFFFFFFFF is overflowing a 32-bit value. Use `SDL_UINT64_C(0xFFFFFFFF1)`
370
 * instead of `0xFFFFFFFF1` by itself.
371
 *
372
 * \since This macro is available since SDL 3.2.0.
373
 *
374
 * \sa SDL_SINT64_C
375
 */
376
#define SDL_UINT64_C(c)  c ## ULL /* or whatever the current compiler uses. */
377
378
#else /* !SDL_WIKI_DOCUMENTATION_SECTION */
379
380
#ifndef SDL_SINT64_C
381
#if defined(INT64_C)
382
#define SDL_SINT64_C(c)  INT64_C(c)
383
#elif defined(_MSC_VER)
384
#define SDL_SINT64_C(c)  c ## i64
385
#elif defined(__LP64__) || defined(_LP64)
386
#define SDL_SINT64_C(c)  c ## L
387
#else
388
#define SDL_SINT64_C(c)  c ## LL
389
#endif
390
#endif /* !SDL_SINT64_C */
391
392
#ifndef SDL_UINT64_C
393
#if defined(UINT64_C)
394
80.5k
#define SDL_UINT64_C(c)  UINT64_C(c)
395
#elif defined(_MSC_VER)
396
#define SDL_UINT64_C(c)  c ## ui64
397
#elif defined(__LP64__) || defined(_LP64)
398
#define SDL_UINT64_C(c)  c ## UL
399
#else
400
#define SDL_UINT64_C(c)  c ## ULL
401
#endif
402
#endif /* !SDL_UINT64_C */
403
404
#endif /* !SDL_WIKI_DOCUMENTATION_SECTION */
405
406
/**
407
 *  \name Basic data types
408
 */
409
/* @{ */
410
411
/**
412
 * A signed 8-bit integer type.
413
 *
414
 * \since This macro is available since SDL 3.2.0.
415
 */
416
typedef int8_t Sint8;
417
#define SDL_MAX_SINT8   ((Sint8)0x7F)           /* 127 */
418
#define SDL_MIN_SINT8   ((Sint8)(~0x7F))        /* -128 */
419
420
/**
421
 * An unsigned 8-bit integer type.
422
 *
423
 * \since This macro is available since SDL 3.2.0.
424
 */
425
typedef uint8_t Uint8;
426
#define SDL_MAX_UINT8   ((Uint8)0xFF)           /* 255 */
427
#define SDL_MIN_UINT8   ((Uint8)0x00)           /* 0 */
428
429
/**
430
 * A signed 16-bit integer type.
431
 *
432
 * \since This macro is available since SDL 3.2.0.
433
 */
434
typedef int16_t Sint16;
435
#define SDL_MAX_SINT16  ((Sint16)0x7FFF)        /* 32767 */
436
#define SDL_MIN_SINT16  ((Sint16)(~0x7FFF))     /* -32768 */
437
438
/**
439
 * An unsigned 16-bit integer type.
440
 *
441
 * \since This macro is available since SDL 3.2.0.
442
 */
443
typedef uint16_t Uint16;
444
#define SDL_MAX_UINT16  ((Uint16)0xFFFF)        /* 65535 */
445
#define SDL_MIN_UINT16  ((Uint16)0x0000)        /* 0 */
446
447
/**
448
 * A signed 32-bit integer type.
449
 *
450
 * \since This macro is available since SDL 3.2.0.
451
 */
452
typedef int32_t Sint32;
453
#define SDL_MAX_SINT32  ((Sint32)0x7FFFFFFF)    /* 2147483647 */
454
#define SDL_MIN_SINT32  ((Sint32)(~0x7FFFFFFF)) /* -2147483648 */
455
456
/**
457
 * An unsigned 32-bit integer type.
458
 *
459
 * \since This macro is available since SDL 3.2.0.
460
 */
461
typedef uint32_t Uint32;
462
#define SDL_MAX_UINT32  ((Uint32)0xFFFFFFFFu)   /* 4294967295 */
463
#define SDL_MIN_UINT32  ((Uint32)0x00000000)    /* 0 */
464
465
/**
466
 * A signed 64-bit integer type.
467
 *
468
 * \since This macro is available since SDL 3.2.0.
469
 *
470
 * \sa SDL_SINT64_C
471
 */
472
typedef int64_t Sint64;
473
#define SDL_MAX_SINT64  SDL_SINT64_C(0x7FFFFFFFFFFFFFFF)   /* 9223372036854775807 */
474
#define SDL_MIN_SINT64  ~SDL_SINT64_C(0x7FFFFFFFFFFFFFFF)  /* -9223372036854775808 */
475
476
/**
477
 * An unsigned 64-bit integer type.
478
 *
479
 * \since This macro is available since SDL 3.2.0.
480
 *
481
 * \sa SDL_UINT64_C
482
 */
483
typedef uint64_t Uint64;
484
#define SDL_MAX_UINT64  SDL_UINT64_C(0xFFFFFFFFFFFFFFFF)   /* 18446744073709551615 */
485
#define SDL_MIN_UINT64  SDL_UINT64_C(0x0000000000000000)   /* 0 */
486
487
/**
488
 * SDL times are signed, 64-bit integers representing nanoseconds since the
489
 * Unix epoch (Jan 1, 1970).
490
 *
491
 * They can be converted between POSIX time_t values with SDL_NS_TO_SECONDS()
492
 * and SDL_SECONDS_TO_NS(), and between Windows FILETIME values with
493
 * SDL_TimeToWindows() and SDL_TimeFromWindows().
494
 *
495
 * \since This macro is available since SDL 3.2.0.
496
 *
497
 * \sa SDL_MAX_SINT64
498
 * \sa SDL_MIN_SINT64
499
 */
500
typedef Sint64 SDL_Time;
501
#define SDL_MAX_TIME SDL_MAX_SINT64
502
#define SDL_MIN_TIME SDL_MIN_SINT64
503
504
/* @} *//* Basic data types */
505
506
/**
507
 *  \name Floating-point constants
508
 */
509
/* @{ */
510
511
#ifdef FLT_EPSILON
512
#define SDL_FLT_EPSILON FLT_EPSILON
513
#else
514
515
/**
516
 * Epsilon constant, used for comparing floating-point numbers.
517
 *
518
 * Equals by default to platform-defined `FLT_EPSILON`, or
519
 * `1.1920928955078125e-07F` if that's not available.
520
 *
521
 * \since This macro is available since SDL 3.2.0.
522
 */
523
#define SDL_FLT_EPSILON 1.1920928955078125e-07F /* 0x0.000002p0 */
524
#endif
525
526
/* @} *//* Floating-point constants */
527
528
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
529
530
/**
531
 * A printf-formatting string for an Sint64 value.
532
 *
533
 * Use it like this:
534
 *
535
 * ```c
536
 * SDL_Log("There are %" SDL_PRIs64 " bottles of beer on the wall.", bottles);
537
 * ```
538
 *
539
 * \since This macro is available since SDL 3.2.0.
540
 */
541
#define SDL_PRIs64 "lld"
542
543
/**
544
 * A printf-formatting string for a Uint64 value.
545
 *
546
 * Use it like this:
547
 *
548
 * ```c
549
 * SDL_Log("There are %" SDL_PRIu64 " bottles of beer on the wall.", bottles);
550
 * ```
551
 *
552
 * \since This macro is available since SDL 3.2.0.
553
 */
554
#define SDL_PRIu64 "llu"
555
556
/**
557
 * A printf-formatting string for a Uint64 value as lower-case hexadecimal.
558
 *
559
 * Use it like this:
560
 *
561
 * ```c
562
 * SDL_Log("There are %" SDL_PRIx64 " bottles of beer on the wall.", bottles);
563
 * ```
564
 *
565
 * \since This macro is available since SDL 3.2.0.
566
 */
567
#define SDL_PRIx64 "llx"
568
569
/**
570
 * A printf-formatting string for a Uint64 value as upper-case hexadecimal.
571
 *
572
 * Use it like this:
573
 *
574
 * ```c
575
 * SDL_Log("There are %" SDL_PRIX64 " bottles of beer on the wall.", bottles);
576
 * ```
577
 *
578
 * \since This macro is available since SDL 3.2.0.
579
 */
580
#define SDL_PRIX64 "llX"
581
582
/**
583
 * A printf-formatting string for an Sint32 value.
584
 *
585
 * Use it like this:
586
 *
587
 * ```c
588
 * SDL_Log("There are %" SDL_PRIs32 " bottles of beer on the wall.", bottles);
589
 * ```
590
 *
591
 * \since This macro is available since SDL 3.2.0.
592
 */
593
#define SDL_PRIs32 "d"
594
595
/**
596
 * A printf-formatting string for a Uint32 value.
597
 *
598
 * Use it like this:
599
 *
600
 * ```c
601
 * SDL_Log("There are %" SDL_PRIu32 " bottles of beer on the wall.", bottles);
602
 * ```
603
 *
604
 * \since This macro is available since SDL 3.2.0.
605
 */
606
#define SDL_PRIu32 "u"
607
608
/**
609
 * A printf-formatting string for a Uint32 value as lower-case hexadecimal.
610
 *
611
 * Use it like this:
612
 *
613
 * ```c
614
 * SDL_Log("There are %" SDL_PRIx32 " bottles of beer on the wall.", bottles);
615
 * ```
616
 *
617
 * \since This macro is available since SDL 3.2.0.
618
 */
619
#define SDL_PRIx32 "x"
620
621
/**
622
 * A printf-formatting string for a Uint32 value as upper-case hexadecimal.
623
 *
624
 * Use it like this:
625
 *
626
 * ```c
627
 * SDL_Log("There are %" SDL_PRIX32 " bottles of beer on the wall.", bottles);
628
 * ```
629
 *
630
 * \since This macro is available since SDL 3.2.0.
631
 */
632
#define SDL_PRIX32 "X"
633
634
/**
635
 * A printf-formatting string prefix for a `long long` value.
636
 *
637
 * This is just the prefix! You probably actually want SDL_PRILLd, SDL_PRILLu,
638
 * SDL_PRILLx, or SDL_PRILLX instead.
639
 *
640
 * Use it like this:
641
 *
642
 * ```c
643
 * SDL_Log("There are %" SDL_PRILL_PREFIX "d bottles of beer on the wall.", bottles);
644
 * ```
645
 *
646
 * \since This macro is available since SDL 3.2.0.
647
 */
648
#define SDL_PRILL_PREFIX "ll"
649
650
/**
651
 * A printf-formatting string for a `long long` value.
652
 *
653
 * Use it like this:
654
 *
655
 * ```c
656
 * SDL_Log("There are %" SDL_PRILLd " bottles of beer on the wall.", bottles);
657
 * ```
658
 *
659
 * \since This macro is available since SDL 3.2.0.
660
 */
661
#define SDL_PRILLd SDL_PRILL_PREFIX "d"
662
663
/**
664
 * A printf-formatting string for a `unsigned long long` value.
665
 *
666
 * Use it like this:
667
 *
668
 * ```c
669
 * SDL_Log("There are %" SDL_PRILLu " bottles of beer on the wall.", bottles);
670
 * ```
671
 *
672
 * \since This macro is available since SDL 3.2.0.
673
 */
674
#define SDL_PRILLu SDL_PRILL_PREFIX "u"
675
676
/**
677
 * A printf-formatting string for an `unsigned long long` value as lower-case
678
 * hexadecimal.
679
 *
680
 * Use it like this:
681
 *
682
 * ```c
683
 * SDL_Log("There are %" SDL_PRILLx " bottles of beer on the wall.", bottles);
684
 * ```
685
 *
686
 * \since This macro is available since SDL 3.2.0.
687
 */
688
#define SDL_PRILLx SDL_PRILL_PREFIX "x"
689
690
/**
691
 * A printf-formatting string for an `unsigned long long` value as upper-case
692
 * hexadecimal.
693
 *
694
 * Use it like this:
695
 *
696
 * ```c
697
 * SDL_Log("There are %" SDL_PRILLX " bottles of beer on the wall.", bottles);
698
 * ```
699
 *
700
 * \since This macro is available since SDL 3.2.0.
701
 */
702
#define SDL_PRILLX SDL_PRILL_PREFIX "X"
703
#endif /* SDL_WIKI_DOCUMENTATION_SECTION */
704
705
/* Make sure we have macros for printing width-based integers.
706
 * <inttypes.h> should define these but this is not true all platforms.
707
 * (for example win32) */
708
#ifndef SDL_PRIs64
709
#if defined(SDL_PLATFORM_WINDOWS)
710
#define SDL_PRIs64 "I64d"
711
#elif defined(PRId64)
712
#define SDL_PRIs64 PRId64
713
#elif defined(__LP64__) && !defined(SDL_PLATFORM_APPLE) && !defined(__EMSCRIPTEN__)
714
#define SDL_PRIs64 "ld"
715
#else
716
#define SDL_PRIs64 "lld"
717
#endif
718
#endif
719
#ifndef SDL_PRIu64
720
#if defined(SDL_PLATFORM_WINDOWS)
721
#define SDL_PRIu64 "I64u"
722
#elif defined(PRIu64)
723
#define SDL_PRIu64 PRIu64
724
#elif defined(__LP64__) && !defined(SDL_PLATFORM_APPLE) && !defined(__EMSCRIPTEN__)
725
#define SDL_PRIu64 "lu"
726
#else
727
#define SDL_PRIu64 "llu"
728
#endif
729
#endif
730
#ifndef SDL_PRIx64
731
#if defined(SDL_PLATFORM_WINDOWS)
732
#define SDL_PRIx64 "I64x"
733
#elif defined(PRIx64)
734
#define SDL_PRIx64 PRIx64
735
#elif defined(__LP64__) && !defined(SDL_PLATFORM_APPLE)
736
#define SDL_PRIx64 "lx"
737
#else
738
#define SDL_PRIx64 "llx"
739
#endif
740
#endif
741
#ifndef SDL_PRIX64
742
#if defined(SDL_PLATFORM_WINDOWS)
743
#define SDL_PRIX64 "I64X"
744
#elif defined(PRIX64)
745
#define SDL_PRIX64 PRIX64
746
#elif defined(__LP64__) && !defined(SDL_PLATFORM_APPLE)
747
#define SDL_PRIX64 "lX"
748
#else
749
#define SDL_PRIX64 "llX"
750
#endif
751
#endif
752
#ifndef SDL_PRIs32
753
#ifdef PRId32
754
#define SDL_PRIs32 PRId32
755
#else
756
#define SDL_PRIs32 "d"
757
#endif
758
#endif
759
#ifndef SDL_PRIu32
760
#ifdef PRIu32
761
#define SDL_PRIu32 PRIu32
762
#else
763
#define SDL_PRIu32 "u"
764
#endif
765
#endif
766
#ifndef SDL_PRIx32
767
#ifdef PRIx32
768
#define SDL_PRIx32 PRIx32
769
#else
770
#define SDL_PRIx32 "x"
771
#endif
772
#endif
773
#ifndef SDL_PRIX32
774
#ifdef PRIX32
775
#define SDL_PRIX32 PRIX32
776
#else
777
#define SDL_PRIX32 "X"
778
#endif
779
#endif
780
/* Specifically for the `long long` -- SDL-specific. */
781
#ifdef SDL_PLATFORM_WINDOWS
782
#ifndef SDL_NOLONGLONG
783
SDL_COMPILE_TIME_ASSERT(longlong_size64, sizeof(long long) == 8); /* using I64 for windows - make sure `long long` is 64 bits. */
784
#endif
785
#define SDL_PRILL_PREFIX "I64"
786
#else
787
#define SDL_PRILL_PREFIX "ll"
788
#endif
789
#ifndef SDL_PRILLd
790
#define SDL_PRILLd SDL_PRILL_PREFIX "d"
791
#endif
792
#ifndef SDL_PRILLu
793
#define SDL_PRILLu SDL_PRILL_PREFIX "u"
794
#endif
795
#ifndef SDL_PRILLx
796
#define SDL_PRILLx SDL_PRILL_PREFIX "x"
797
#endif
798
#ifndef SDL_PRILLX
799
#define SDL_PRILLX SDL_PRILL_PREFIX "X"
800
#endif
801
802
/* Annotations to help code analysis tools */
803
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
804
805
/**
806
 * Macro that annotates function params with input buffer size.
807
 *
808
 * If we were to annotate `memcpy`:
809
 *
810
 * ```c
811
 * void *memcpy(void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
812
 * ```
813
 *
814
 * This notes that `src` should be `len` bytes in size and is only read by the
815
 * function. The compiler or other analysis tools can warn when this doesn't
816
 * appear to be the case.
817
 *
818
 * On compilers without this annotation mechanism, this is defined to nothing.
819
 *
820
 * \since This macro is available since SDL 3.2.0.
821
 */
822
#define SDL_IN_BYTECAP(x) _In_bytecount_(x)
823
824
/**
825
 * Macro that annotates function params with input/output string buffer size.
826
 *
827
 * If we were to annotate `strlcat`:
828
 *
829
 * ```c
830
 * size_t strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen);
831
 * ```
832
 *
833
 * This notes that `dst` is a null-terminated C string, should be `maxlen`
834
 * bytes in size, and is both read from and written to by the function. The
835
 * compiler or other analysis tools can warn when this doesn't appear to be
836
 * the case.
837
 *
838
 * On compilers without this annotation mechanism, this is defined to nothing.
839
 *
840
 * \since This macro is available since SDL 3.2.0.
841
 */
842
#define SDL_INOUT_Z_CAP(x) _Inout_z_cap_(x)
843
844
/**
845
 * Macro that annotates function params with output string buffer size.
846
 *
847
 * If we were to annotate `snprintf`:
848
 *
849
 * ```c
850
 * int snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, const char *fmt, ...);
851
 * ```
852
 *
853
 * This notes that `text` is a null-terminated C string, should be `maxlen`
854
 * bytes in size, and is only written to by the function. The compiler or
855
 * other analysis tools can warn when this doesn't appear to be the case.
856
 *
857
 * On compilers without this annotation mechanism, this is defined to nothing.
858
 *
859
 * \since This macro is available since SDL 3.2.0.
860
 */
861
#define SDL_OUT_Z_CAP(x) _Out_z_cap_(x)
862
863
/**
864
 * Macro that annotates function params with output buffer size.
865
 *
866
 * If we were to annotate `wcsncpy`:
867
 *
868
 * ```c
869
 * char *wcscpy(SDL_OUT_CAP(bufsize) wchar_t *dst, const wchar_t *src, size_t bufsize);
870
 * ```
871
 *
872
 * This notes that `dst` should have a capacity of `bufsize` wchar_t in size,
873
 * and is only written to by the function. The compiler or other analysis
874
 * tools can warn when this doesn't appear to be the case.
875
 *
876
 * This operates on counts of objects, not bytes. Use SDL_OUT_BYTECAP for
877
 * bytes.
878
 *
879
 * On compilers without this annotation mechanism, this is defined to nothing.
880
 *
881
 * \since This macro is available since SDL 3.2.0.
882
 */
883
#define SDL_OUT_CAP(x) _Out_cap_(x)
884
885
/**
886
 * Macro that annotates function params with output buffer size.
887
 *
888
 * If we were to annotate `memcpy`:
889
 *
890
 * ```c
891
 * void *memcpy(SDL_OUT_BYTECAP(bufsize) void *dst, const void *src, size_t bufsize);
892
 * ```
893
 *
894
 * This notes that `dst` should have a capacity of `bufsize` bytes in size,
895
 * and is only written to by the function. The compiler or other analysis
896
 * tools can warn when this doesn't appear to be the case.
897
 *
898
 * On compilers without this annotation mechanism, this is defined to nothing.
899
 *
900
 * \since This macro is available since SDL 3.2.0.
901
 */
902
#define SDL_OUT_BYTECAP(x) _Out_bytecap_(x)
903
904
/**
905
 * Macro that annotates function params with output buffer string size.
906
 *
907
 * If we were to annotate `strcpy`:
908
 *
909
 * ```c
910
 * char *strcpy(SDL_OUT_Z_BYTECAP(bufsize) char *dst, const char *src, size_t bufsize);
911
 * ```
912
 *
913
 * This notes that `dst` should have a capacity of `bufsize` bytes in size,
914
 * and a zero-terminated string is written to it by the function. The compiler
915
 * or other analysis tools can warn when this doesn't appear to be the case.
916
 *
917
 * On compilers without this annotation mechanism, this is defined to nothing.
918
 *
919
 * \since This macro is available since SDL 3.2.0.
920
 */
921
#define SDL_OUT_Z_BYTECAP(x) _Out_z_bytecap_(x)
922
923
/**
924
 * Macro that annotates function params as printf-style format strings.
925
 *
926
 * If we were to annotate `fprintf`:
927
 *
928
 * ```c
929
 * int fprintf(FILE *f, SDL_PRINTF_FORMAT_STRING const char *fmt, ...);
930
 * ```
931
 *
932
 * This notes that `fmt` should be a printf-style format string. The compiler
933
 * or other analysis tools can warn when this doesn't appear to be the case.
934
 *
935
 * On compilers without this annotation mechanism, this is defined to nothing.
936
 *
937
 * \since This macro is available since SDL 3.2.0.
938
 */
939
#define SDL_PRINTF_FORMAT_STRING _Printf_format_string_
940
941
/**
942
 * Macro that annotates function params as scanf-style format strings.
943
 *
944
 * If we were to annotate `fscanf`:
945
 *
946
 * ```c
947
 * int fscanf(FILE *f, SDL_SCANF_FORMAT_STRING const char *fmt, ...);
948
 * ```
949
 *
950
 * This notes that `fmt` should be a scanf-style format string. The compiler
951
 * or other analysis tools can warn when this doesn't appear to be the case.
952
 *
953
 * On compilers without this annotation mechanism, this is defined to nothing.
954
 *
955
 * \since This macro is available since SDL 3.2.0.
956
 */
957
#define SDL_SCANF_FORMAT_STRING _Scanf_format_string_impl_
958
959
/**
960
 * Macro that annotates a vararg function that operates like printf.
961
 *
962
 * If we were to annotate `fprintf`:
963
 *
964
 * ```c
965
 * int fprintf(FILE *f, const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(2);
966
 * ```
967
 *
968
 * This notes that the second parameter should be a printf-style format
969
 * string, followed by `...`. The compiler or other analysis tools can warn
970
 * when this doesn't appear to be the case.
971
 *
972
 * On compilers without this annotation mechanism, this is defined to nothing.
973
 *
974
 * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which
975
 * between them will cover at least Visual Studio, GCC, and Clang.
976
 *
977
 * \since This macro is available since SDL 3.2.0.
978
 */
979
#define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 )))
980
981
/**
982
 * Macro that annotates a va_list function that operates like printf.
983
 *
984
 * If we were to annotate `vfprintf`:
985
 *
986
 * ```c
987
 * int vfprintf(FILE *f, const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2);
988
 * ```
989
 *
990
 * This notes that the second parameter should be a printf-style format
991
 * string, followed by a va_list. The compiler or other analysis tools can
992
 * warn when this doesn't appear to be the case.
993
 *
994
 * On compilers without this annotation mechanism, this is defined to nothing.
995
 *
996
 * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which
997
 * between them will cover at least Visual Studio, GCC, and Clang.
998
 *
999
 * \since This macro is available since SDL 3.2.0.
1000
 */
1001
#define SDL_PRINTF_VARARG_FUNCV( fmtargnumber ) __attribute__(( format( __printf__, fmtargnumber, 0 )))
1002
1003
/**
1004
 * Macro that annotates a vararg function that operates like scanf.
1005
 *
1006
 * If we were to annotate `fscanf`:
1007
 *
1008
 * ```c
1009
 * int fscanf(FILE *f, const char *fmt, ...) SDL_PRINTF_VARARG_FUNCV(2);
1010
 * ```
1011
 *
1012
 * This notes that the second parameter should be a scanf-style format string,
1013
 * followed by `...`. The compiler or other analysis tools can warn when this
1014
 * doesn't appear to be the case.
1015
 *
1016
 * On compilers without this annotation mechanism, this is defined to nothing.
1017
 *
1018
 * This can (and should) be used with SDL_SCANF_FORMAT_STRING as well, which
1019
 * between them will cover at least Visual Studio, GCC, and Clang.
1020
 *
1021
 * \since This macro is available since SDL 3.2.0.
1022
 */
1023
#define SDL_SCANF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __scanf__, fmtargnumber, fmtargnumber+1 )))
1024
1025
/**
1026
 * Macro that annotates a va_list function that operates like scanf.
1027
 *
1028
 * If we were to annotate `vfscanf`:
1029
 *
1030
 * ```c
1031
 * int vfscanf(FILE *f, const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2);
1032
 * ```
1033
 *
1034
 * This notes that the second parameter should be a scanf-style format string,
1035
 * followed by a va_list. The compiler or other analysis tools can warn when
1036
 * this doesn't appear to be the case.
1037
 *
1038
 * On compilers without this annotation mechanism, this is defined to nothing.
1039
 *
1040
 * This can (and should) be used with SDL_SCANF_FORMAT_STRING as well, which
1041
 * between them will cover at least Visual Studio, GCC, and Clang.
1042
 *
1043
 * \since This macro is available since SDL 3.2.0.
1044
 */
1045
#define SDL_SCANF_VARARG_FUNCV( fmtargnumber ) __attribute__(( format( __scanf__, fmtargnumber, 0 )))
1046
1047
/**
1048
 * Macro that annotates a vararg function that operates like wprintf.
1049
 *
1050
 * If we were to annotate `fwprintf`:
1051
 *
1052
 * ```c
1053
 * int fwprintf(FILE *f, const wchar_t *fmt, ...) SDL_WPRINTF_VARARG_FUNC(2);
1054
 * ```
1055
 *
1056
 * This notes that the second parameter should be a wprintf-style format wide
1057
 * string, followed by `...`. The compiler or other analysis tools can warn
1058
 * when this doesn't appear to be the case.
1059
 *
1060
 * On compilers without this annotation mechanism, this is defined to nothing.
1061
 *
1062
 * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which
1063
 * between them will cover at least Visual Studio, GCC, and Clang.
1064
 *
1065
 * \since This macro is available since SDL 3.2.0.
1066
 */
1067
#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, fmtargnumber+1 ))) */
1068
1069
/**
1070
 * Macro that annotates a va_list function that operates like wprintf.
1071
 *
1072
 * If we were to annotate `vfwprintf`:
1073
 *
1074
 * ```c
1075
 * int vfwprintf(FILE *f, const wchar_t *fmt, va_list ap) SDL_WPRINTF_VARARG_FUNC(2);
1076
 * ```
1077
 *
1078
 * This notes that the second parameter should be a wprintf-style format wide
1079
 * string, followed by a va_list. The compiler or other analysis tools can
1080
 * warn when this doesn't appear to be the case.
1081
 *
1082
 * On compilers without this annotation mechanism, this is defined to nothing.
1083
 *
1084
 * This can (and should) be used with SDL_PRINTF_FORMAT_STRING as well, which
1085
 * between them will cover at least Visual Studio, GCC, and Clang.
1086
 *
1087
 * \since This macro is available since SDL 3.2.0.
1088
 */
1089
#define SDL_WPRINTF_VARARG_FUNCV( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, 0 ))) */
1090
1091
#elif defined(SDL_DISABLE_ANALYZE_MACROS)
1092
#define SDL_IN_BYTECAP(x)
1093
#define SDL_INOUT_Z_CAP(x)
1094
#define SDL_OUT_Z_CAP(x)
1095
#define SDL_OUT_CAP(x)
1096
#define SDL_OUT_BYTECAP(x)
1097
#define SDL_OUT_Z_BYTECAP(x)
1098
#define SDL_PRINTF_FORMAT_STRING
1099
#define SDL_SCANF_FORMAT_STRING
1100
#define SDL_PRINTF_VARARG_FUNC( fmtargnumber )
1101
#define SDL_PRINTF_VARARG_FUNCV( fmtargnumber )
1102
#define SDL_SCANF_VARARG_FUNC( fmtargnumber )
1103
#define SDL_SCANF_VARARG_FUNCV( fmtargnumber )
1104
#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber )
1105
#define SDL_WPRINTF_VARARG_FUNCV( fmtargnumber )
1106
#else
1107
#if defined(_MSC_VER) && (_MSC_VER >= 1600) /* VS 2010 and above */
1108
#include <sal.h>
1109
1110
#define SDL_IN_BYTECAP(x) _In_bytecount_(x)
1111
#define SDL_INOUT_Z_CAP(x) _Inout_z_cap_(x)
1112
#define SDL_OUT_Z_CAP(x) _Out_z_cap_(x)
1113
#define SDL_OUT_CAP(x) _Out_cap_(x)
1114
#define SDL_OUT_BYTECAP(x) _Out_bytecap_(x)
1115
#define SDL_OUT_Z_BYTECAP(x) _Out_z_bytecap_(x)
1116
1117
#define SDL_PRINTF_FORMAT_STRING _Printf_format_string_
1118
#define SDL_SCANF_FORMAT_STRING _Scanf_format_string_impl_
1119
#else
1120
#define SDL_IN_BYTECAP(x)
1121
#define SDL_INOUT_Z_CAP(x)
1122
#define SDL_OUT_Z_CAP(x)
1123
#define SDL_OUT_CAP(x)
1124
#define SDL_OUT_BYTECAP(x)
1125
#define SDL_OUT_Z_BYTECAP(x)
1126
#define SDL_PRINTF_FORMAT_STRING
1127
#define SDL_SCANF_FORMAT_STRING
1128
#endif
1129
#if defined(__GNUC__) || defined(__clang__)
1130
#define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 )))
1131
#define SDL_PRINTF_VARARG_FUNCV( fmtargnumber ) __attribute__(( format( __printf__, fmtargnumber, 0 )))
1132
#define SDL_SCANF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __scanf__, fmtargnumber, fmtargnumber+1 )))
1133
#define SDL_SCANF_VARARG_FUNCV( fmtargnumber ) __attribute__(( format( __scanf__, fmtargnumber, 0 )))
1134
#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, fmtargnumber+1 ))) */
1135
#define SDL_WPRINTF_VARARG_FUNCV( fmtargnumber ) /* __attribute__ (( format( __wprintf__, fmtargnumber, 0 ))) */
1136
#else
1137
#define SDL_PRINTF_VARARG_FUNC( fmtargnumber )
1138
#define SDL_PRINTF_VARARG_FUNCV( fmtargnumber )
1139
#define SDL_SCANF_VARARG_FUNC( fmtargnumber )
1140
#define SDL_SCANF_VARARG_FUNCV( fmtargnumber )
1141
#define SDL_WPRINTF_VARARG_FUNC( fmtargnumber )
1142
#define SDL_WPRINTF_VARARG_FUNCV( fmtargnumber )
1143
#endif
1144
#endif /* SDL_DISABLE_ANALYZE_MACROS */
1145
1146
/** \cond */
1147
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
1148
SDL_COMPILE_TIME_ASSERT(bool_size, sizeof(bool) == 1);
1149
SDL_COMPILE_TIME_ASSERT(uint8_size, sizeof(Uint8) == 1);
1150
SDL_COMPILE_TIME_ASSERT(sint8_size, sizeof(Sint8) == 1);
1151
SDL_COMPILE_TIME_ASSERT(uint16_size, sizeof(Uint16) == 2);
1152
SDL_COMPILE_TIME_ASSERT(sint16_size, sizeof(Sint16) == 2);
1153
SDL_COMPILE_TIME_ASSERT(uint32_size, sizeof(Uint32) == 4);
1154
SDL_COMPILE_TIME_ASSERT(sint32_size, sizeof(Sint32) == 4);
1155
SDL_COMPILE_TIME_ASSERT(uint64_size, sizeof(Uint64) == 8);
1156
SDL_COMPILE_TIME_ASSERT(sint64_size, sizeof(Sint64) == 8);
1157
#ifndef SDL_NOLONGLONG
1158
SDL_COMPILE_TIME_ASSERT(uint64_longlong, sizeof(Uint64) <= sizeof(unsigned long long));
1159
SDL_COMPILE_TIME_ASSERT(size_t_longlong, sizeof(size_t) <= sizeof(unsigned long long));
1160
#endif
1161
typedef struct SDL_alignment_test
1162
{
1163
    Uint8 a;
1164
    void *b;
1165
} SDL_alignment_test;
1166
SDL_COMPILE_TIME_ASSERT(struct_alignment, sizeof(SDL_alignment_test) == (2 * sizeof(void *)));
1167
SDL_COMPILE_TIME_ASSERT(two_s_complement, (int)~(int)0 == (int)(-1));
1168
#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
1169
/** \endcond */
1170
1171
/* Check to make sure enums are the size of ints, for structure packing.
1172
   For both Watcom C/C++ and Borland C/C++ the compiler option that makes
1173
   enums having the size of an int must be enabled.
1174
   This is "-b" for Borland C/C++ and "-ei" for Watcom C/C++ (v11).
1175
*/
1176
1177
/** \cond */
1178
#ifndef DOXYGEN_SHOULD_IGNORE_THIS
1179
#if !defined(SDL_PLATFORM_VITA) && !defined(SDL_PLATFORM_3DS)
1180
/* TODO: include/SDL_stdinc.h:390: error: size of array 'SDL_dummy_enum' is negative */
1181
typedef enum SDL_DUMMY_ENUM
1182
{
1183
    DUMMY_ENUM_VALUE
1184
} SDL_DUMMY_ENUM;
1185
1186
SDL_COMPILE_TIME_ASSERT(enum, sizeof(SDL_DUMMY_ENUM) == sizeof(int));
1187
#endif
1188
#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
1189
/** \endcond */
1190
1191
#include <SDL3/SDL_begin_code.h>
1192
/* Set up for C function definitions, even when using C++ */
1193
#ifdef __cplusplus
1194
extern "C" {
1195
#endif
1196
1197
/**
1198
 * A macro to initialize an SDL interface.
1199
 *
1200
 * This macro will initialize an SDL interface structure and should be called
1201
 * before you fill out the fields with your implementation.
1202
 *
1203
 * You can use it like this:
1204
 *
1205
 * ```c
1206
 * SDL_IOStreamInterface iface;
1207
 *
1208
 * SDL_INIT_INTERFACE(&iface);
1209
 *
1210
 * // Fill in the interface function pointers with your implementation
1211
 * iface.seek = ...
1212
 *
1213
 * stream = SDL_OpenIO(&iface, NULL);
1214
 * ```
1215
 *
1216
 * If you are using designated initializers, you can use the size of the
1217
 * interface as the version, e.g.
1218
 *
1219
 * ```c
1220
 * SDL_IOStreamInterface iface = {
1221
 *     .version = sizeof(iface),
1222
 *     .seek = ...
1223
 * };
1224
 * stream = SDL_OpenIO(&iface, NULL);
1225
 * ```
1226
 *
1227
 * \threadsafety It is safe to call this macro from any thread.
1228
 *
1229
 * \since This macro is available since SDL 3.2.0.
1230
 *
1231
 * \sa SDL_IOStreamInterface
1232
 * \sa SDL_StorageInterface
1233
 * \sa SDL_VirtualJoystickDesc
1234
 */
1235
#define SDL_INIT_INTERFACE(iface)               \
1236
    do {                                        \
1237
        SDL_zerop(iface);                       \
1238
        (iface)->version = sizeof(*(iface));    \
1239
    } while (0)
1240
1241
1242
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
1243
1244
/**
1245
 * Allocate memory on the stack (maybe).
1246
 *
1247
 * If SDL knows how to access alloca() on the current platform, it will use it
1248
 * to stack-allocate memory here. If it doesn't, it will use SDL_malloc() to
1249
 * heap-allocate memory.
1250
 *
1251
 * Since this might not be stack memory at all, it's important that you check
1252
 * the returned pointer for NULL, and that you call SDL_stack_free on the
1253
 * memory when done with it. Since this might be stack memory, it's important
1254
 * that you don't allocate large amounts of it, or allocate in a loop without
1255
 * returning from the function, so the stack doesn't overflow.
1256
 *
1257
 * \param type the datatype of the memory to allocate.
1258
 * \param count the number of `type` objects to allocate.
1259
 * \returns newly-allocated memory, or NULL on failure.
1260
 *
1261
 * \threadsafety It is safe to call this macro from any thread.
1262
 *
1263
 * \since This macro is available since SDL 3.2.0.
1264
 *
1265
 * \sa SDL_stack_free
1266
 */
1267
#define SDL_stack_alloc(type, count)    (type*)alloca(sizeof(type)*(count))
1268
1269
/**
1270
 * Free memory previously allocated with SDL_stack_alloc.
1271
 *
1272
 * If SDL used alloca() to allocate this memory, this macro does nothing and
1273
 * the allocated memory will be automatically released when the function that
1274
 * called SDL_stack_alloc() returns. If SDL used SDL_malloc(), it will
1275
 * SDL_free the memory immediately.
1276
 *
1277
 * \param data the pointer, from SDL_stack_alloc(), to free.
1278
 *
1279
 * \threadsafety It is safe to call this macro from any thread.
1280
 *
1281
 * \since This macro is available since SDL 3.2.0.
1282
 *
1283
 * \sa SDL_stack_alloc
1284
 */
1285
#define SDL_stack_free(data)
1286
#elif !defined(SDL_DISABLE_ALLOCA)
1287
#define SDL_stack_alloc(type, count)    (type*)alloca(sizeof(type)*(count))
1288
#define SDL_stack_free(data)
1289
#else
1290
#define SDL_stack_alloc(type, count)    (type*)SDL_malloc(sizeof(type)*(count))
1291
#define SDL_stack_free(data)            SDL_free(data)
1292
#endif
1293
1294
/**
1295
 * Allocate uninitialized memory.
1296
 *
1297
 * The allocated memory returned by this function must be freed with
1298
 * SDL_free().
1299
 *
1300
 * If `size` is 0, it will be set to 1.
1301
 *
1302
 * If the allocation is successful, the returned pointer is guaranteed to be
1303
 * aligned to either the *fundamental alignment* (`alignof(max_align_t)` in
1304
 * C11 and later) or `2 * sizeof(void *)`, whichever is smaller. Use
1305
 * SDL_aligned_alloc() if you need to allocate memory aligned to an alignment
1306
 * greater than this guarantee.
1307
 *
1308
 * \param size the size to allocate.
1309
 * \returns a pointer to the allocated memory, or NULL if allocation failed.
1310
 *
1311
 * \threadsafety It is safe to call this function from any thread.
1312
 *
1313
 * \since This function is available since SDL 3.2.0.
1314
 *
1315
 * \sa SDL_free
1316
 * \sa SDL_calloc
1317
 * \sa SDL_realloc
1318
 * \sa SDL_aligned_alloc
1319
 */
1320
extern SDL_DECLSPEC SDL_MALLOC void * SDLCALL SDL_malloc(size_t size);
1321
1322
/**
1323
 * Allocate a zero-initialized array.
1324
 *
1325
 * The memory returned by this function must be freed with SDL_free().
1326
 *
1327
 * If either of `nmemb` or `size` is 0, they will both be set to 1.
1328
 *
1329
 * If the allocation is successful, the returned pointer is guaranteed to be
1330
 * aligned to either the *fundamental alignment* (`alignof(max_align_t)` in
1331
 * C11 and later) or `2 * sizeof(void *)`, whichever is smaller.
1332
 *
1333
 * \param nmemb the number of elements in the array.
1334
 * \param size the size of each element of the array.
1335
 * \returns a pointer to the allocated array, or NULL if allocation failed.
1336
 *
1337
 * \threadsafety It is safe to call this function from any thread.
1338
 *
1339
 * \since This function is available since SDL 3.2.0.
1340
 *
1341
 * \sa SDL_free
1342
 * \sa SDL_malloc
1343
 * \sa SDL_realloc
1344
 */
1345
extern SDL_DECLSPEC SDL_MALLOC SDL_ALLOC_SIZE2(1, 2) void * SDLCALL SDL_calloc(size_t nmemb, size_t size);
1346
1347
/**
1348
 * Change the size of allocated memory.
1349
 *
1350
 * The memory returned by this function must be freed with SDL_free().
1351
 *
1352
 * If `size` is 0, it will be set to 1. Note that this is unlike some other C
1353
 * runtime `realloc` implementations, which may treat `realloc(mem, 0)` the
1354
 * same way as `free(mem)`.
1355
 *
1356
 * If `mem` is NULL, the behavior of this function is equivalent to
1357
 * SDL_malloc(). Otherwise, the function can have one of three possible
1358
 * outcomes:
1359
 *
1360
 * - If it returns the same pointer as `mem`, it means that `mem` was resized
1361
 *   in place without freeing.
1362
 * - If it returns a different non-NULL pointer, it means that `mem` was freed
1363
 *   and cannot be dereferenced anymore.
1364
 * - If it returns NULL (indicating failure), then `mem` will remain valid and
1365
 *   must still be freed with SDL_free().
1366
 *
1367
 * If the allocation is successfully resized, the returned pointer is
1368
 * guaranteed to be aligned to either the *fundamental alignment*
1369
 * (`alignof(max_align_t)` in C11 and later) or `2 * sizeof(void *)`,
1370
 * whichever is smaller.
1371
 *
1372
 * \param mem a pointer to allocated memory to reallocate, or NULL.
1373
 * \param size the new size of the memory.
1374
 * \returns a pointer to the newly allocated memory, or NULL if allocation
1375
 *          failed.
1376
 *
1377
 * \threadsafety It is safe to call this function from any thread.
1378
 *
1379
 * \since This function is available since SDL 3.2.0.
1380
 *
1381
 * \sa SDL_free
1382
 * \sa SDL_malloc
1383
 * \sa SDL_calloc
1384
 */
1385
extern SDL_DECLSPEC SDL_ALLOC_SIZE(2) void * SDLCALL SDL_realloc(void *mem, size_t size);
1386
1387
/**
1388
 * Free allocated memory.
1389
 *
1390
 * The pointer is no longer valid after this call and cannot be dereferenced
1391
 * anymore.
1392
 *
1393
 * If `mem` is NULL, this function does nothing.
1394
 *
1395
 * \param mem a pointer to allocated memory, or NULL.
1396
 *
1397
 * \threadsafety It is safe to call this function from any thread.
1398
 *
1399
 * \since This function is available since SDL 3.2.0.
1400
 *
1401
 * \sa SDL_malloc
1402
 * \sa SDL_calloc
1403
 * \sa SDL_realloc
1404
 */
1405
extern SDL_DECLSPEC void SDLCALL SDL_free(void *mem);
1406
1407
/**
1408
 * A callback used to implement SDL_malloc().
1409
 *
1410
 * SDL will always ensure that the passed `size` is greater than 0.
1411
 *
1412
 * \param size the size to allocate.
1413
 * \returns a pointer to the allocated memory, or NULL if allocation failed.
1414
 *
1415
 * \threadsafety It should be safe to call this callback from any thread.
1416
 *
1417
 * \since This datatype is available since SDL 3.2.0.
1418
 *
1419
 * \sa SDL_malloc
1420
 * \sa SDL_GetOriginalMemoryFunctions
1421
 * \sa SDL_GetMemoryFunctions
1422
 * \sa SDL_SetMemoryFunctions
1423
 */
1424
typedef void *(SDLCALL *SDL_malloc_func)(size_t size);
1425
1426
/**
1427
 * A callback used to implement SDL_calloc().
1428
 *
1429
 * SDL will always ensure that the passed `nmemb` and `size` are both greater
1430
 * than 0.
1431
 *
1432
 * \param nmemb the number of elements in the array.
1433
 * \param size the size of each element of the array.
1434
 * \returns a pointer to the allocated array, or NULL if allocation failed.
1435
 *
1436
 * \threadsafety It should be safe to call this callback from any thread.
1437
 *
1438
 * \since This datatype is available since SDL 3.2.0.
1439
 *
1440
 * \sa SDL_calloc
1441
 * \sa SDL_GetOriginalMemoryFunctions
1442
 * \sa SDL_GetMemoryFunctions
1443
 * \sa SDL_SetMemoryFunctions
1444
 */
1445
typedef void *(SDLCALL *SDL_calloc_func)(size_t nmemb, size_t size);
1446
1447
/**
1448
 * A callback used to implement SDL_realloc().
1449
 *
1450
 * SDL will always ensure that the passed `size` is greater than 0.
1451
 *
1452
 * \param mem a pointer to allocated memory to reallocate, or NULL.
1453
 * \param size the new size of the memory.
1454
 * \returns a pointer to the newly allocated memory, or NULL if allocation
1455
 *          failed.
1456
 *
1457
 * \threadsafety It should be safe to call this callback from any thread.
1458
 *
1459
 * \since This datatype is available since SDL 3.2.0.
1460
 *
1461
 * \sa SDL_realloc
1462
 * \sa SDL_GetOriginalMemoryFunctions
1463
 * \sa SDL_GetMemoryFunctions
1464
 * \sa SDL_SetMemoryFunctions
1465
 */
1466
typedef void *(SDLCALL *SDL_realloc_func)(void *mem, size_t size);
1467
1468
/**
1469
 * A callback used to implement SDL_free().
1470
 *
1471
 * SDL will always ensure that the passed `mem` is a non-NULL pointer.
1472
 *
1473
 * \param mem a pointer to allocated memory.
1474
 *
1475
 * \threadsafety It should be safe to call this callback from any thread.
1476
 *
1477
 * \since This datatype is available since SDL 3.2.0.
1478
 *
1479
 * \sa SDL_free
1480
 * \sa SDL_GetOriginalMemoryFunctions
1481
 * \sa SDL_GetMemoryFunctions
1482
 * \sa SDL_SetMemoryFunctions
1483
 */
1484
typedef void (SDLCALL *SDL_free_func)(void *mem);
1485
1486
/**
1487
 * Get the original set of SDL memory functions.
1488
 *
1489
 * This is what SDL_malloc and friends will use by default, if there has been
1490
 * no call to SDL_SetMemoryFunctions. This is not necessarily using the C
1491
 * runtime's `malloc` functions behind the scenes! Different platforms and
1492
 * build configurations might do any number of unexpected things.
1493
 *
1494
 * \param malloc_func filled with malloc function.
1495
 * \param calloc_func filled with calloc function.
1496
 * \param realloc_func filled with realloc function.
1497
 * \param free_func filled with free function.
1498
 *
1499
 * \threadsafety It is safe to call this function from any thread.
1500
 *
1501
 * \since This function is available since SDL 3.2.0.
1502
 */
1503
extern SDL_DECLSPEC void SDLCALL SDL_GetOriginalMemoryFunctions(SDL_malloc_func *malloc_func,
1504
                                                            SDL_calloc_func *calloc_func,
1505
                                                            SDL_realloc_func *realloc_func,
1506
                                                            SDL_free_func *free_func);
1507
1508
/**
1509
 * Get the current set of SDL memory functions.
1510
 *
1511
 * \param malloc_func filled with malloc function.
1512
 * \param calloc_func filled with calloc function.
1513
 * \param realloc_func filled with realloc function.
1514
 * \param free_func filled with free function.
1515
 *
1516
 * \threadsafety This does not hold a lock, so do not call this in the
1517
 *               unlikely event of a background thread calling
1518
 *               SDL_SetMemoryFunctions simultaneously.
1519
 *
1520
 * \since This function is available since SDL 3.2.0.
1521
 *
1522
 * \sa SDL_SetMemoryFunctions
1523
 * \sa SDL_GetOriginalMemoryFunctions
1524
 */
1525
extern SDL_DECLSPEC void SDLCALL SDL_GetMemoryFunctions(SDL_malloc_func *malloc_func,
1526
                                                    SDL_calloc_func *calloc_func,
1527
                                                    SDL_realloc_func *realloc_func,
1528
                                                    SDL_free_func *free_func);
1529
1530
/**
1531
 * Replace SDL's memory allocation functions with a custom set.
1532
 *
1533
 * It is not safe to call this function once any allocations have been made,
1534
 * as future calls to SDL_free will use the new allocator, even if they came
1535
 * from an SDL_malloc made with the old one!
1536
 *
1537
 * If used, usually this needs to be the first call made into the SDL library,
1538
 * if not the very first thing done at program startup time.
1539
 *
1540
 * \param malloc_func custom malloc function.
1541
 * \param calloc_func custom calloc function.
1542
 * \param realloc_func custom realloc function.
1543
 * \param free_func custom free function.
1544
 * \returns true on success or false on failure; call SDL_GetError() for more
1545
 *          information.
1546
 *
1547
 * \threadsafety It is safe to call this function from any thread, but one
1548
 *               should not replace the memory functions once any allocations
1549
 *               are made!
1550
 *
1551
 * \since This function is available since SDL 3.2.0.
1552
 *
1553
 * \sa SDL_GetMemoryFunctions
1554
 * \sa SDL_GetOriginalMemoryFunctions
1555
 */
1556
extern SDL_DECLSPEC bool SDLCALL SDL_SetMemoryFunctions(SDL_malloc_func malloc_func,
1557
                                                            SDL_calloc_func calloc_func,
1558
                                                            SDL_realloc_func realloc_func,
1559
                                                            SDL_free_func free_func);
1560
1561
/**
1562
 * Allocate memory aligned to a specific alignment.
1563
 *
1564
 * The memory returned by this function must be freed with SDL_aligned_free(),
1565
 * _not_ SDL_free().
1566
 *
1567
 * If `alignment` is less than the size of `void *`, it will be increased to
1568
 * match that.
1569
 *
1570
 * The returned memory address will be a multiple of the alignment value, and
1571
 * the size of the memory allocated will be a multiple of the alignment value.
1572
 *
1573
 * \param alignment the alignment of the memory.
1574
 * \param size the size to allocate.
1575
 * \returns a pointer to the aligned memory, or NULL if allocation failed.
1576
 *
1577
 * \threadsafety It is safe to call this function from any thread.
1578
 *
1579
 * \since This function is available since SDL 3.2.0.
1580
 *
1581
 * \sa SDL_aligned_free
1582
 */
1583
extern SDL_DECLSPEC SDL_MALLOC void * SDLCALL SDL_aligned_alloc(size_t alignment, size_t size);
1584
1585
/**
1586
 * Free memory allocated by SDL_aligned_alloc().
1587
 *
1588
 * The pointer is no longer valid after this call and cannot be dereferenced
1589
 * anymore.
1590
 *
1591
 * If `mem` is NULL, this function does nothing.
1592
 *
1593
 * \param mem a pointer previously returned by SDL_aligned_alloc(), or NULL.
1594
 *
1595
 * \threadsafety It is safe to call this function from any thread.
1596
 *
1597
 * \since This function is available since SDL 3.2.0.
1598
 *
1599
 * \sa SDL_aligned_alloc
1600
 */
1601
extern SDL_DECLSPEC void SDLCALL SDL_aligned_free(void *mem);
1602
1603
/**
1604
 * Get the number of outstanding (unfreed) allocations.
1605
 *
1606
 * \returns the number of allocations or -1 if allocation counting is
1607
 *          disabled.
1608
 *
1609
 * \threadsafety It is safe to call this function from any thread.
1610
 *
1611
 * \since This function is available since SDL 3.2.0.
1612
 */
1613
extern SDL_DECLSPEC int SDLCALL SDL_GetNumAllocations(void);
1614
1615
/**
1616
 * A thread-safe set of environment variables
1617
 *
1618
 * \since This struct is available since SDL 3.2.0.
1619
 *
1620
 * \sa SDL_GetEnvironment
1621
 * \sa SDL_CreateEnvironment
1622
 * \sa SDL_GetEnvironmentVariable
1623
 * \sa SDL_GetEnvironmentVariables
1624
 * \sa SDL_SetEnvironmentVariable
1625
 * \sa SDL_UnsetEnvironmentVariable
1626
 * \sa SDL_DestroyEnvironment
1627
 */
1628
typedef struct SDL_Environment SDL_Environment;
1629
1630
/**
1631
 * Get the process environment.
1632
 *
1633
 * This is initialized at application start and is not affected by setenv()
1634
 * and unsetenv() calls after that point. Use SDL_SetEnvironmentVariable() and
1635
 * SDL_UnsetEnvironmentVariable() if you want to modify this environment, or
1636
 * SDL_setenv_unsafe() or SDL_unsetenv_unsafe() if you want changes to persist
1637
 * in the C runtime environment after SDL_Quit().
1638
 *
1639
 * \returns a pointer to the environment for the process or NULL on failure;
1640
 *          call SDL_GetError() for more information.
1641
 *
1642
 * \threadsafety It is safe to call this function from any thread.
1643
 *
1644
 * \since This function is available since SDL 3.2.0.
1645
 *
1646
 * \sa SDL_GetEnvironmentVariable
1647
 * \sa SDL_GetEnvironmentVariables
1648
 * \sa SDL_SetEnvironmentVariable
1649
 * \sa SDL_UnsetEnvironmentVariable
1650
 */
1651
extern SDL_DECLSPEC SDL_Environment * SDLCALL SDL_GetEnvironment(void);
1652
1653
/**
1654
 * Create a set of environment variables
1655
 *
1656
 * \param populated true to initialize it from the C runtime environment,
1657
 *                  false to create an empty environment.
1658
 * \returns a pointer to the new environment or NULL on failure; call
1659
 *          SDL_GetError() for more information.
1660
 *
1661
 * \threadsafety If `populated` is false, it is safe to call this function
1662
 *               from any thread, otherwise it is safe if no other threads are
1663
 *               calling setenv() or unsetenv()
1664
 *
1665
 * \since This function is available since SDL 3.2.0.
1666
 *
1667
 * \sa SDL_GetEnvironmentVariable
1668
 * \sa SDL_GetEnvironmentVariables
1669
 * \sa SDL_SetEnvironmentVariable
1670
 * \sa SDL_UnsetEnvironmentVariable
1671
 * \sa SDL_DestroyEnvironment
1672
 */
1673
extern SDL_DECLSPEC SDL_Environment * SDLCALL SDL_CreateEnvironment(bool populated);
1674
1675
/**
1676
 * Get the value of a variable in the environment.
1677
 *
1678
 * \param env the environment to query.
1679
 * \param name the name of the variable to get.
1680
 * \returns a pointer to the value of the variable or NULL if it can't be
1681
 *          found.
1682
 *
1683
 * \threadsafety It is safe to call this function from any thread.
1684
 *
1685
 * \since This function is available since SDL 3.2.0.
1686
 *
1687
 * \sa SDL_GetEnvironment
1688
 * \sa SDL_CreateEnvironment
1689
 * \sa SDL_GetEnvironmentVariables
1690
 * \sa SDL_SetEnvironmentVariable
1691
 * \sa SDL_UnsetEnvironmentVariable
1692
 */
1693
extern SDL_DECLSPEC const char * SDLCALL SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name);
1694
1695
/**
1696
 * Get all variables in the environment.
1697
 *
1698
 * \param env the environment to query.
1699
 * \returns a NULL terminated array of pointers to environment variables in
1700
 *          the form "variable=value" or NULL on failure; call SDL_GetError()
1701
 *          for more information. This is a single allocation that should be
1702
 *          freed with SDL_free() when it is no longer needed.
1703
 *
1704
 * \threadsafety It is safe to call this function from any thread.
1705
 *
1706
 * \since This function is available since SDL 3.2.0.
1707
 *
1708
 * \sa SDL_GetEnvironment
1709
 * \sa SDL_CreateEnvironment
1710
 * \sa SDL_GetEnvironmentVariables
1711
 * \sa SDL_SetEnvironmentVariable
1712
 * \sa SDL_UnsetEnvironmentVariable
1713
 */
1714
extern SDL_DECLSPEC char ** SDLCALL SDL_GetEnvironmentVariables(SDL_Environment *env);
1715
1716
/**
1717
 * Set the value of a variable in the environment.
1718
 *
1719
 * \param env the environment to modify.
1720
 * \param name the name of the variable to set.
1721
 * \param value the value of the variable to set.
1722
 * \param overwrite true to overwrite the variable if it exists, false to
1723
 *                  return success without setting the variable if it already
1724
 *                  exists.
1725
 * \returns true on success or false on failure; call SDL_GetError() for more
1726
 *          information.
1727
 *
1728
 * \threadsafety It is safe to call this function from any thread.
1729
 *
1730
 * \since This function is available since SDL 3.2.0.
1731
 *
1732
 * \sa SDL_GetEnvironment
1733
 * \sa SDL_CreateEnvironment
1734
 * \sa SDL_GetEnvironmentVariable
1735
 * \sa SDL_GetEnvironmentVariables
1736
 * \sa SDL_UnsetEnvironmentVariable
1737
 */
1738
extern SDL_DECLSPEC bool SDLCALL SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, bool overwrite);
1739
1740
/**
1741
 * Clear a variable from the environment.
1742
 *
1743
 * \param env the environment to modify.
1744
 * \param name the name of the variable to unset.
1745
 * \returns true on success or false on failure; call SDL_GetError() for more
1746
 *          information.
1747
 *
1748
 * \threadsafety It is safe to call this function from any thread.
1749
 *
1750
 * \since This function is available since SDL 3.2.0.
1751
 *
1752
 * \sa SDL_GetEnvironment
1753
 * \sa SDL_CreateEnvironment
1754
 * \sa SDL_GetEnvironmentVariable
1755
 * \sa SDL_GetEnvironmentVariables
1756
 * \sa SDL_SetEnvironmentVariable
1757
 * \sa SDL_UnsetEnvironmentVariable
1758
 */
1759
extern SDL_DECLSPEC bool SDLCALL SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name);
1760
1761
/**
1762
 * Destroy a set of environment variables.
1763
 *
1764
 * \param env the environment to destroy.
1765
 *
1766
 * \threadsafety It is safe to call this function from any thread, as long as
1767
 *               the environment is no longer in use.
1768
 *
1769
 * \since This function is available since SDL 3.2.0.
1770
 *
1771
 * \sa SDL_CreateEnvironment
1772
 */
1773
extern SDL_DECLSPEC void SDLCALL SDL_DestroyEnvironment(SDL_Environment *env);
1774
1775
/**
1776
 * Get the value of a variable in the environment.
1777
 *
1778
 * This function uses SDL's cached copy of the environment and is thread-safe.
1779
 *
1780
 * \param name the name of the variable to get.
1781
 * \returns a pointer to the value of the variable or NULL if it can't be
1782
 *          found.
1783
 *
1784
 * \threadsafety It is safe to call this function from any thread.
1785
 *
1786
 * \since This function is available since SDL 3.2.0.
1787
 */
1788
extern SDL_DECLSPEC const char * SDLCALL SDL_getenv(const char *name);
1789
1790
/**
1791
 * Get the value of a variable in the environment.
1792
 *
1793
 * This function bypasses SDL's cached copy of the environment and is not
1794
 * thread-safe.
1795
 *
1796
 * \param name the name of the variable to get.
1797
 * \returns a pointer to the value of the variable or NULL if it can't be
1798
 *          found.
1799
 *
1800
 * \threadsafety This function is not thread safe, consider using SDL_getenv()
1801
 *               instead.
1802
 *
1803
 * \since This function is available since SDL 3.2.0.
1804
 *
1805
 * \sa SDL_getenv
1806
 */
1807
extern SDL_DECLSPEC const char * SDLCALL SDL_getenv_unsafe(const char *name);
1808
1809
/**
1810
 * Set the value of a variable in the environment.
1811
 *
1812
 * \param name the name of the variable to set.
1813
 * \param value the value of the variable to set.
1814
 * \param overwrite 1 to overwrite the variable if it exists, 0 to return
1815
 *                  success without setting the variable if it already exists.
1816
 * \returns 0 on success, -1 on error.
1817
 *
1818
 * \threadsafety This function is not thread safe, consider using
1819
 *               SDL_SetEnvironmentVariable() instead.
1820
 *
1821
 * \since This function is available since SDL 3.2.0.
1822
 *
1823
 * \sa SDL_SetEnvironmentVariable
1824
 */
1825
extern SDL_DECLSPEC int SDLCALL SDL_setenv_unsafe(const char *name, const char *value, int overwrite);
1826
1827
/**
1828
 * Clear a variable from the environment.
1829
 *
1830
 * \param name the name of the variable to unset.
1831
 * \returns 0 on success, -1 on error.
1832
 *
1833
 * \threadsafety This function is not thread safe, consider using
1834
 *               SDL_UnsetEnvironmentVariable() instead.
1835
 *
1836
 * \since This function is available since SDL 3.2.0.
1837
 *
1838
 * \sa SDL_UnsetEnvironmentVariable
1839
 */
1840
extern SDL_DECLSPEC int SDLCALL SDL_unsetenv_unsafe(const char *name);
1841
1842
/**
1843
 * A callback used with SDL sorting and binary search functions.
1844
 *
1845
 * \param a a pointer to the first element being compared.
1846
 * \param b a pointer to the second element being compared.
1847
 * \returns -1 if `a` should be sorted before `b`, 1 if `b` should be sorted
1848
 *          before `a`, 0 if they are equal. If two elements are equal, their
1849
 *          order in the sorted array is undefined.
1850
 *
1851
 * \since This callback is available since SDL 3.2.0.
1852
 *
1853
 * \sa SDL_bsearch
1854
 * \sa SDL_qsort
1855
 */
1856
typedef int (SDLCALL *SDL_CompareCallback)(const void *a, const void *b);
1857
1858
/**
1859
 * Sort an array.
1860
 *
1861
 * For example:
1862
 *
1863
 * ```c
1864
 * typedef struct {
1865
 *     int key;
1866
 *     const char *string;
1867
 * } data;
1868
 *
1869
 * int SDLCALL compare(const void *a, const void *b)
1870
 * {
1871
 *     const data *A = (const data *)a;
1872
 *     const data *B = (const data *)b;
1873
 *
1874
 *     if (A->n < B->n) {
1875
 *         return -1;
1876
 *     } else if (B->n < A->n) {
1877
 *         return 1;
1878
 *     } else {
1879
 *         return 0;
1880
 *     }
1881
 * }
1882
 *
1883
 * data values[] = {
1884
 *     { 3, "third" }, { 1, "first" }, { 2, "second" }
1885
 * };
1886
 *
1887
 * SDL_qsort(values, SDL_arraysize(values), sizeof(values[0]), compare);
1888
 * ```
1889
 *
1890
 * \param base a pointer to the start of the array.
1891
 * \param nmemb the number of elements in the array.
1892
 * \param size the size of the elements in the array.
1893
 * \param compare a function used to compare elements in the array.
1894
 *
1895
 * \threadsafety It is safe to call this function from any thread.
1896
 *
1897
 * \since This function is available since SDL 3.2.0.
1898
 *
1899
 * \sa SDL_bsearch
1900
 * \sa SDL_qsort_r
1901
 */
1902
extern SDL_DECLSPEC void SDLCALL SDL_qsort(void *base, size_t nmemb, size_t size, SDL_CompareCallback compare);
1903
1904
/**
1905
 * Perform a binary search on a previously sorted array.
1906
 *
1907
 * For example:
1908
 *
1909
 * ```c
1910
 * typedef struct {
1911
 *     int key;
1912
 *     const char *string;
1913
 * } data;
1914
 *
1915
 * int SDLCALL compare(const void *a, const void *b)
1916
 * {
1917
 *     const data *A = (const data *)a;
1918
 *     const data *B = (const data *)b;
1919
 *
1920
 *     if (A->n < B->n) {
1921
 *         return -1;
1922
 *     } else if (B->n < A->n) {
1923
 *         return 1;
1924
 *     } else {
1925
 *         return 0;
1926
 *     }
1927
 * }
1928
 *
1929
 * data values[] = {
1930
 *     { 1, "first" }, { 2, "second" }, { 3, "third" }
1931
 * };
1932
 * data key = { 2, NULL };
1933
 *
1934
 * data *result = SDL_bsearch(&key, values, SDL_arraysize(values), sizeof(values[0]), compare);
1935
 * ```
1936
 *
1937
 * \param key a pointer to a key equal to the element being searched for.
1938
 * \param base a pointer to the start of the array.
1939
 * \param nmemb the number of elements in the array.
1940
 * \param size the size of the elements in the array.
1941
 * \param compare a function used to compare elements in the array.
1942
 * \returns a pointer to the matching element in the array, or NULL if not
1943
 *          found.
1944
 *
1945
 * \threadsafety It is safe to call this function from any thread.
1946
 *
1947
 * \since This function is available since SDL 3.2.0.
1948
 *
1949
 * \sa SDL_bsearch_r
1950
 * \sa SDL_qsort
1951
 */
1952
extern SDL_DECLSPEC void * SDLCALL SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, SDL_CompareCallback compare);
1953
1954
/**
1955
 * A callback used with SDL sorting and binary search functions.
1956
 *
1957
 * \param userdata the `userdata` pointer passed to the sort function.
1958
 * \param a a pointer to the first element being compared.
1959
 * \param b a pointer to the second element being compared.
1960
 * \returns -1 if `a` should be sorted before `b`, 1 if `b` should be sorted
1961
 *          before `a`, 0 if they are equal. If two elements are equal, their
1962
 *          order in the sorted array is undefined.
1963
 *
1964
 * \since This callback is available since SDL 3.2.0.
1965
 *
1966
 * \sa SDL_qsort_r
1967
 * \sa SDL_bsearch_r
1968
 */
1969
typedef int (SDLCALL *SDL_CompareCallback_r)(void *userdata, const void *a, const void *b);
1970
1971
/**
1972
 * Sort an array, passing a userdata pointer to the compare function.
1973
 *
1974
 * For example:
1975
 *
1976
 * ```c
1977
 * typedef enum {
1978
 *     sort_increasing,
1979
 *     sort_decreasing,
1980
 * } sort_method;
1981
 *
1982
 * typedef struct {
1983
 *     int key;
1984
 *     const char *string;
1985
 * } data;
1986
 *
1987
 * int SDLCALL compare(const void *userdata, const void *a, const void *b)
1988
 * {
1989
 *     sort_method method = (sort_method)(uintptr_t)userdata;
1990
 *     const data *A = (const data *)a;
1991
 *     const data *B = (const data *)b;
1992
 *
1993
 *     if (A->key < B->key) {
1994
 *         return (method == sort_increasing) ? -1 : 1;
1995
 *     } else if (B->key < A->key) {
1996
 *         return (method == sort_increasing) ? 1 : -1;
1997
 *     } else {
1998
 *         return 0;
1999
 *     }
2000
 * }
2001
 *
2002
 * data values[] = {
2003
 *     { 3, "third" }, { 1, "first" }, { 2, "second" }
2004
 * };
2005
 *
2006
 * SDL_qsort_r(values, SDL_arraysize(values), sizeof(values[0]), compare, (const void *)(uintptr_t)sort_increasing);
2007
 * ```
2008
 *
2009
 * \param base a pointer to the start of the array.
2010
 * \param nmemb the number of elements in the array.
2011
 * \param size the size of the elements in the array.
2012
 * \param compare a function used to compare elements in the array.
2013
 * \param userdata a pointer to pass to the compare function.
2014
 *
2015
 * \threadsafety It is safe to call this function from any thread.
2016
 *
2017
 * \since This function is available since SDL 3.2.0.
2018
 *
2019
 * \sa SDL_bsearch_r
2020
 * \sa SDL_qsort
2021
 */
2022
extern SDL_DECLSPEC void SDLCALL SDL_qsort_r(void *base, size_t nmemb, size_t size, SDL_CompareCallback_r compare, void *userdata);
2023
2024
/**
2025
 * Perform a binary search on a previously sorted array, passing a userdata
2026
 * pointer to the compare function.
2027
 *
2028
 * For example:
2029
 *
2030
 * ```c
2031
 * typedef enum {
2032
 *     sort_increasing,
2033
 *     sort_decreasing,
2034
 * } sort_method;
2035
 *
2036
 * typedef struct {
2037
 *     int key;
2038
 *     const char *string;
2039
 * } data;
2040
 *
2041
 * int SDLCALL compare(const void *userdata, const void *a, const void *b)
2042
 * {
2043
 *     sort_method method = (sort_method)(uintptr_t)userdata;
2044
 *     const data *A = (const data *)a;
2045
 *     const data *B = (const data *)b;
2046
 *
2047
 *     if (A->key < B->key) {
2048
 *         return (method == sort_increasing) ? -1 : 1;
2049
 *     } else if (B->key < A->key) {
2050
 *         return (method == sort_increasing) ? 1 : -1;
2051
 *     } else {
2052
 *         return 0;
2053
 *     }
2054
 * }
2055
 *
2056
 * data values[] = {
2057
 *     { 1, "first" }, { 2, "second" }, { 3, "third" }
2058
 * };
2059
 * data key = { 2, NULL };
2060
 *
2061
 * data *result = SDL_bsearch_r(&key, values, SDL_arraysize(values), sizeof(values[0]), compare, (const void *)(uintptr_t)sort_increasing);
2062
 * ```
2063
 *
2064
 * \param key a pointer to a key equal to the element being searched for.
2065
 * \param base a pointer to the start of the array.
2066
 * \param nmemb the number of elements in the array.
2067
 * \param size the size of the elements in the array.
2068
 * \param compare a function used to compare elements in the array.
2069
 * \param userdata a pointer to pass to the compare function.
2070
 * \returns a pointer to the matching element in the array, or NULL if not
2071
 *          found.
2072
 *
2073
 * \threadsafety It is safe to call this function from any thread.
2074
 *
2075
 * \since This function is available since SDL 3.2.0.
2076
 *
2077
 * \sa SDL_bsearch
2078
 * \sa SDL_qsort_r
2079
 */
2080
extern SDL_DECLSPEC void * SDLCALL SDL_bsearch_r(const void *key, const void *base, size_t nmemb, size_t size, SDL_CompareCallback_r compare, void *userdata);
2081
2082
/**
2083
 * Compute the absolute value of `x`.
2084
 *
2085
 * \param x an integer value.
2086
 * \returns the absolute value of x.
2087
 *
2088
 * \threadsafety It is safe to call this function from any thread.
2089
 *
2090
 * \since This function is available since SDL 3.2.0.
2091
 */
2092
extern SDL_DECLSPEC int SDLCALL SDL_abs(int x);
2093
2094
/**
2095
 * Return the lesser of two values.
2096
 *
2097
 * This is a helper macro that might be more clear than writing out the
2098
 * comparisons directly, and works with any type that can be compared with the
2099
 * `<` operator. However, it double-evaluates both its parameters, so do not
2100
 * use expressions with side-effects here.
2101
 *
2102
 * \param x the first value to compare.
2103
 * \param y the second value to compare.
2104
 * \returns the lesser of `x` and `y`.
2105
 *
2106
 * \threadsafety It is safe to call this macro from any thread.
2107
 *
2108
 * \since This macro is available since SDL 3.2.0.
2109
 */
2110
#define SDL_min(x, y) (((x) < (y)) ? (x) : (y))
2111
2112
/**
2113
 * Return the greater of two values.
2114
 *
2115
 * This is a helper macro that might be more clear than writing out the
2116
 * comparisons directly, and works with any type that can be compared with the
2117
 * `>` operator. However, it double-evaluates both its parameters, so do not
2118
 * use expressions with side-effects here.
2119
 *
2120
 * \param x the first value to compare.
2121
 * \param y the second value to compare.
2122
 * \returns the lesser of `x` and `y`.
2123
 *
2124
 * \threadsafety It is safe to call this macro from any thread.
2125
 *
2126
 * \since This macro is available since SDL 3.2.0.
2127
 */
2128
#define SDL_max(x, y) (((x) > (y)) ? (x) : (y))
2129
2130
/**
2131
 * Return a value clamped to a range.
2132
 *
2133
 * If `x` is outside the range a values between `a` and `b`, the returned
2134
 * value will be `a` or `b` as appropriate. Otherwise, `x` is returned.
2135
 *
2136
 * This macro will produce incorrect results if `b` is less than `a`.
2137
 *
2138
 * This is a helper macro that might be more clear than writing out the
2139
 * comparisons directly, and works with any type that can be compared with the
2140
 * `<` and `>` operators. However, it double-evaluates all its parameters, so
2141
 * do not use expressions with side-effects here.
2142
 *
2143
 * \param x the value to compare.
2144
 * \param a the low end value.
2145
 * \param b the high end value.
2146
 * \returns x, clamped between a and b.
2147
 *
2148
 * \threadsafety It is safe to call this macro from any thread.
2149
 *
2150
 * \since This macro is available since SDL 3.2.0.
2151
 */
2152
#define SDL_clamp(x, a, b) (((x) < (a)) ? (a) : (((x) > (b)) ? (b) : (x)))
2153
2154
/**
2155
 * Query if a character is alphabetic (a letter).
2156
 *
2157
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2158
 * for English 'a-z' and 'A-Z' as true.
2159
 *
2160
 * \param x character value to check.
2161
 * \returns non-zero if x falls within the character class, zero otherwise.
2162
 *
2163
 * \threadsafety It is safe to call this function from any thread.
2164
 *
2165
 * \since This function is available since SDL 3.2.0.
2166
 */
2167
extern SDL_DECLSPEC int SDLCALL SDL_isalpha(int x);
2168
2169
/**
2170
 * Query if a character is alphabetic (a letter) or a number.
2171
 *
2172
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2173
 * for English 'a-z', 'A-Z', and '0-9' as true.
2174
 *
2175
 * \param x character value to check.
2176
 * \returns non-zero if x falls within the character class, zero otherwise.
2177
 *
2178
 * \threadsafety It is safe to call this function from any thread.
2179
 *
2180
 * \since This function is available since SDL 3.2.0.
2181
 */
2182
extern SDL_DECLSPEC int SDLCALL SDL_isalnum(int x);
2183
2184
/**
2185
 * Report if a character is blank (a space or tab).
2186
 *
2187
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2188
 * 0x20 (space) or 0x9 (tab) as true.
2189
 *
2190
 * \param x character value to check.
2191
 * \returns non-zero if x falls within the character class, zero otherwise.
2192
 *
2193
 * \threadsafety It is safe to call this function from any thread.
2194
 *
2195
 * \since This function is available since SDL 3.2.0.
2196
 */
2197
extern SDL_DECLSPEC int SDLCALL SDL_isblank(int x);
2198
2199
/**
2200
 * Report if a character is a control character.
2201
 *
2202
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2203
 * 0 through 0x1F, and 0x7F, as true.
2204
 *
2205
 * \param x character value to check.
2206
 * \returns non-zero if x falls within the character class, zero otherwise.
2207
 *
2208
 * \threadsafety It is safe to call this function from any thread.
2209
 *
2210
 * \since This function is available since SDL 3.2.0.
2211
 */
2212
extern SDL_DECLSPEC int SDLCALL SDL_iscntrl(int x);
2213
2214
/**
2215
 * Report if a character is a numeric digit.
2216
 *
2217
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2218
 * '0' (0x30) through '9' (0x39), as true.
2219
 *
2220
 * \param x character value to check.
2221
 * \returns non-zero if x falls within the character class, zero otherwise.
2222
 *
2223
 * \threadsafety It is safe to call this function from any thread.
2224
 *
2225
 * \since This function is available since SDL 3.2.0.
2226
 */
2227
extern SDL_DECLSPEC int SDLCALL SDL_isdigit(int x);
2228
2229
/**
2230
 * Report if a character is a hexadecimal digit.
2231
 *
2232
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2233
 * 'A' through 'F', 'a' through 'f', and '0' through '9', as true.
2234
 *
2235
 * \param x character value to check.
2236
 * \returns non-zero if x falls within the character class, zero otherwise.
2237
 *
2238
 * \threadsafety It is safe to call this function from any thread.
2239
 *
2240
 * \since This function is available since SDL 3.2.0.
2241
 */
2242
extern SDL_DECLSPEC int SDLCALL SDL_isxdigit(int x);
2243
2244
/**
2245
 * Report if a character is a punctuation mark.
2246
 *
2247
 * **WARNING**: Regardless of system locale, this is equivalent to
2248
 * `((SDL_isgraph(x)) && (!SDL_isalnum(x)))`.
2249
 *
2250
 * \param x character value to check.
2251
 * \returns non-zero if x falls within the character class, zero otherwise.
2252
 *
2253
 * \threadsafety It is safe to call this function from any thread.
2254
 *
2255
 * \since This function is available since SDL 3.2.0.
2256
 *
2257
 * \sa SDL_isgraph
2258
 * \sa SDL_isalnum
2259
 */
2260
extern SDL_DECLSPEC int SDLCALL SDL_ispunct(int x);
2261
2262
/**
2263
 * Report if a character is whitespace.
2264
 *
2265
 * **WARNING**: Regardless of system locale, this will only treat the
2266
 * following ASCII values as true:
2267
 *
2268
 * - space (0x20)
2269
 * - tab (0x09)
2270
 * - newline (0x0A)
2271
 * - vertical tab (0x0B)
2272
 * - form feed (0x0C)
2273
 * - return (0x0D)
2274
 *
2275
 * \param x character value to check.
2276
 * \returns non-zero if x falls within the character class, zero otherwise.
2277
 *
2278
 * \threadsafety It is safe to call this function from any thread.
2279
 *
2280
 * \since This function is available since SDL 3.2.0.
2281
 */
2282
extern SDL_DECLSPEC int SDLCALL SDL_isspace(int x);
2283
2284
/**
2285
 * Report if a character is upper case.
2286
 *
2287
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2288
 * 'A' through 'Z' as true.
2289
 *
2290
 * \param x character value to check.
2291
 * \returns non-zero if x falls within the character class, zero otherwise.
2292
 *
2293
 * \threadsafety It is safe to call this function from any thread.
2294
 *
2295
 * \since This function is available since SDL 3.2.0.
2296
 */
2297
extern SDL_DECLSPEC int SDLCALL SDL_isupper(int x);
2298
2299
/**
2300
 * Report if a character is lower case.
2301
 *
2302
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2303
 * 'a' through 'z' as true.
2304
 *
2305
 * \param x character value to check.
2306
 * \returns non-zero if x falls within the character class, zero otherwise.
2307
 *
2308
 * \threadsafety It is safe to call this function from any thread.
2309
 *
2310
 * \since This function is available since SDL 3.2.0.
2311
 */
2312
extern SDL_DECLSPEC int SDLCALL SDL_islower(int x);
2313
2314
/**
2315
 * Report if a character is "printable".
2316
 *
2317
 * Be advised that "printable" has a definition that goes back to text
2318
 * terminals from the dawn of computing, making this a sort of special case
2319
 * function that is not suitable for Unicode (or most any) text management.
2320
 *
2321
 * **WARNING**: Regardless of system locale, this will only treat ASCII values
2322
 * ' ' (0x20) through '~' (0x7E) as true.
2323
 *
2324
 * \param x character value to check.
2325
 * \returns non-zero if x falls within the character class, zero otherwise.
2326
 *
2327
 * \threadsafety It is safe to call this function from any thread.
2328
 *
2329
 * \since This function is available since SDL 3.2.0.
2330
 */
2331
extern SDL_DECLSPEC int SDLCALL SDL_isprint(int x);
2332
2333
/**
2334
 * Report if a character is any "printable" except space.
2335
 *
2336
 * Be advised that "printable" has a definition that goes back to text
2337
 * terminals from the dawn of computing, making this a sort of special case
2338
 * function that is not suitable for Unicode (or most any) text management.
2339
 *
2340
 * **WARNING**: Regardless of system locale, this is equivalent to
2341
 * `(SDL_isprint(x)) && ((x) != ' ')`.
2342
 *
2343
 * \param x character value to check.
2344
 * \returns non-zero if x falls within the character class, zero otherwise.
2345
 *
2346
 * \threadsafety It is safe to call this function from any thread.
2347
 *
2348
 * \since This function is available since SDL 3.2.0.
2349
 *
2350
 * \sa SDL_isprint
2351
 */
2352
extern SDL_DECLSPEC int SDLCALL SDL_isgraph(int x);
2353
2354
/**
2355
 * Convert low-ASCII English letters to uppercase.
2356
 *
2357
 * **WARNING**: Regardless of system locale, this will only convert ASCII
2358
 * values 'a' through 'z' to uppercase.
2359
 *
2360
 * This function returns the uppercase equivalent of `x`. If a character
2361
 * cannot be converted, or is already uppercase, this function returns `x`.
2362
 *
2363
 * \param x character value to check.
2364
 * \returns capitalized version of x, or x if no conversion available.
2365
 *
2366
 * \threadsafety It is safe to call this function from any thread.
2367
 *
2368
 * \since This function is available since SDL 3.2.0.
2369
 */
2370
extern SDL_DECLSPEC int SDLCALL SDL_toupper(int x);
2371
2372
/**
2373
 * Convert low-ASCII English letters to lowercase.
2374
 *
2375
 * **WARNING**: Regardless of system locale, this will only convert ASCII
2376
 * values 'A' through 'Z' to lowercase.
2377
 *
2378
 * This function returns the lowercase equivalent of `x`. If a character
2379
 * cannot be converted, or is already lowercase, this function returns `x`.
2380
 *
2381
 * \param x character value to check.
2382
 * \returns lowercase version of x, or x if no conversion available.
2383
 *
2384
 * \threadsafety It is safe to call this function from any thread.
2385
 *
2386
 * \since This function is available since SDL 3.2.0.
2387
 */
2388
extern SDL_DECLSPEC int SDLCALL SDL_tolower(int x);
2389
2390
/**
2391
 * Calculate a CRC-16 value.
2392
 *
2393
 * https://en.wikipedia.org/wiki/Cyclic_redundancy_check
2394
 *
2395
 * This function can be called multiple times, to stream data to be
2396
 * checksummed in blocks. Each call must provide the previous CRC-16 return
2397
 * value to be updated with the next block. The first call to this function
2398
 * for a set of blocks should pass in a zero CRC value.
2399
 *
2400
 * \param crc the current checksum for this data set, or 0 for a new data set.
2401
 * \param data a new block of data to add to the checksum.
2402
 * \param len the size, in bytes, of the new block of data.
2403
 * \returns a CRC-16 checksum value of all blocks in the data set.
2404
 *
2405
 * \threadsafety It is safe to call this function from any thread.
2406
 *
2407
 * \since This function is available since SDL 3.2.0.
2408
 */
2409
extern SDL_DECLSPEC Uint16 SDLCALL SDL_crc16(Uint16 crc, const void *data, size_t len);
2410
2411
/**
2412
 * Calculate a CRC-32 value.
2413
 *
2414
 * https://en.wikipedia.org/wiki/Cyclic_redundancy_check
2415
 *
2416
 * This function can be called multiple times, to stream data to be
2417
 * checksummed in blocks. Each call must provide the previous CRC-32 return
2418
 * value to be updated with the next block. The first call to this function
2419
 * for a set of blocks should pass in a zero CRC value.
2420
 *
2421
 * \param crc the current checksum for this data set, or 0 for a new data set.
2422
 * \param data a new block of data to add to the checksum.
2423
 * \param len the size, in bytes, of the new block of data.
2424
 * \returns a CRC-32 checksum value of all blocks in the data set.
2425
 *
2426
 * \threadsafety It is safe to call this function from any thread.
2427
 *
2428
 * \since This function is available since SDL 3.2.0.
2429
 */
2430
extern SDL_DECLSPEC Uint32 SDLCALL SDL_crc32(Uint32 crc, const void *data, size_t len);
2431
2432
/**
2433
 * Calculate a 32-bit MurmurHash3 value for a block of data.
2434
 *
2435
 * https://en.wikipedia.org/wiki/MurmurHash
2436
 *
2437
 * A seed may be specified, which changes the final results consistently, but
2438
 * this does not work like SDL_crc16 and SDL_crc32: you can't feed a previous
2439
 * result from this function back into itself as the next seed value to
2440
 * calculate a hash in chunks; it won't produce the same hash as it would if
2441
 * the same data was provided in a single call.
2442
 *
2443
 * If you aren't sure what to provide for a seed, zero is fine. Murmur3 is not
2444
 * cryptographically secure, so it shouldn't be used for hashing top-secret
2445
 * data.
2446
 *
2447
 * \param data the data to be hashed.
2448
 * \param len the size of data, in bytes.
2449
 * \param seed a value that alters the final hash value.
2450
 * \returns a Murmur3 32-bit hash value.
2451
 *
2452
 * \threadsafety It is safe to call this function from any thread.
2453
 *
2454
 * \since This function is available since SDL 3.2.0.
2455
 */
2456
extern SDL_DECLSPEC Uint32 SDLCALL SDL_murmur3_32(const void *data, size_t len, Uint32 seed);
2457
2458
/**
2459
 * Copy non-overlapping memory.
2460
 *
2461
 * The memory regions must not overlap. If they do, use SDL_memmove() instead.
2462
 *
2463
 * \param dst The destination memory region. Must not be NULL, and must not
2464
 *            overlap with `src`.
2465
 * \param src The source memory region. Must not be NULL, and must not overlap
2466
 *            with `dst`.
2467
 * \param len The length in bytes of both `dst` and `src`.
2468
 * \returns `dst`.
2469
 *
2470
 * \threadsafety It is safe to call this function from any thread.
2471
 *
2472
 * \since This function is available since SDL 3.2.0.
2473
 *
2474
 * \sa SDL_memmove
2475
 */
2476
extern SDL_DECLSPEC void * SDLCALL SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
2477
2478
/* Take advantage of compiler optimizations for memcpy */
2479
#ifndef SDL_SLOW_MEMCPY
2480
#ifdef SDL_memcpy
2481
#undef SDL_memcpy
2482
#endif
2483
#define SDL_memcpy  memcpy
2484
#endif
2485
2486
2487
/**
2488
 * A macro to copy memory between objects, with basic type checking.
2489
 *
2490
 * SDL_memcpy and SDL_memmove do not care where you copy memory to and from,
2491
 * which can lead to bugs. This macro aims to avoid most of those bugs by
2492
 * making sure that the source and destination are both pointers to objects
2493
 * that are the same size. It does not check that the objects are the same
2494
 * _type_, just that the copy will not overflow either object.
2495
 *
2496
 * The size check happens at compile time, and the compiler will throw an
2497
 * error if the objects are different sizes.
2498
 *
2499
 * Generally this is intended to copy a single object, not an array.
2500
 *
2501
 * This macro looks like it double-evaluates its parameters, but the extras
2502
 * them are in `sizeof` sections, which generate no code nor side-effects.
2503
 *
2504
 * \param dst a pointer to the destination object. Must not be NULL.
2505
 * \param src a pointer to the source object. Must not be NULL.
2506
 *
2507
 * \threadsafety It is safe to call this function from any thread.
2508
 *
2509
 * \since This function is available since SDL 3.2.0.
2510
 */
2511
#define SDL_copyp(dst, src)                                                                 \
2512
    { SDL_COMPILE_TIME_ASSERT(SDL_copyp, sizeof (*(dst)) == sizeof (*(src))); }             \
2513
    SDL_memcpy((dst), (src), sizeof(*(src)))
2514
2515
/**
2516
 * Copy memory ranges that might overlap.
2517
 *
2518
 * It is okay for the memory regions to overlap. If you are confident that the
2519
 * regions never overlap, using SDL_memcpy() may improve performance.
2520
 *
2521
 * \param dst The destination memory region. Must not be NULL.
2522
 * \param src The source memory region. Must not be NULL.
2523
 * \param len The length in bytes of both `dst` and `src`.
2524
 * \returns `dst`.
2525
 *
2526
 * \threadsafety It is safe to call this function from any thread.
2527
 *
2528
 * \since This function is available since SDL 3.2.0.
2529
 *
2530
 * \sa SDL_memcpy
2531
 */
2532
extern SDL_DECLSPEC void * SDLCALL SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len);
2533
2534
/* Take advantage of compiler optimizations for memmove */
2535
#ifndef SDL_SLOW_MEMMOVE
2536
#ifdef SDL_memmove
2537
#undef SDL_memmove
2538
#endif
2539
#define SDL_memmove memmove
2540
#endif
2541
2542
/**
2543
 * Initialize all bytes of buffer of memory to a specific value.
2544
 *
2545
 * This function will set `len` bytes, pointed to by `dst`, to the value
2546
 * specified in `c`.
2547
 *
2548
 * Despite `c` being an `int` instead of a `char`, this only operates on
2549
 * bytes; `c` must be a value between 0 and 255, inclusive.
2550
 *
2551
 * \param dst the destination memory region. Must not be NULL.
2552
 * \param c the byte value to set.
2553
 * \param len the length, in bytes, to set in `dst`.
2554
 * \returns `dst`.
2555
 *
2556
 * \threadsafety It is safe to call this function from any thread.
2557
 *
2558
 * \since This function is available since SDL 3.2.0.
2559
 */
2560
extern SDL_DECLSPEC void * SDLCALL SDL_memset(SDL_OUT_BYTECAP(len) void *dst, int c, size_t len);
2561
2562
/**
2563
 * Initialize all 32-bit words of buffer of memory to a specific value.
2564
 *
2565
 * This function will set a buffer of `dwords` Uint32 values, pointed to by
2566
 * `dst`, to the value specified in `val`.
2567
 *
2568
 * Unlike SDL_memset, this sets 32-bit values, not bytes, so it's not limited
2569
 * to a range of 0-255.
2570
 *
2571
 * \param dst the destination memory region. Must not be NULL.
2572
 * \param val the Uint32 value to set.
2573
 * \param dwords the number of Uint32 values to set in `dst`.
2574
 * \returns `dst`.
2575
 *
2576
 * \threadsafety It is safe to call this function from any thread.
2577
 *
2578
 * \since This function is available since SDL 3.2.0.
2579
 */
2580
extern SDL_DECLSPEC void * SDLCALL SDL_memset4(void *dst, Uint32 val, size_t dwords);
2581
2582
/* Take advantage of compiler optimizations for memset */
2583
#ifndef SDL_SLOW_MEMSET
2584
#ifdef SDL_memset
2585
#undef SDL_memset
2586
#endif
2587
2
#define SDL_memset  memset
2588
#endif
2589
2590
/**
2591
 * Clear an object's memory to zero.
2592
 *
2593
 * This is wrapper over SDL_memset that handles calculating the object size,
2594
 * so there's no chance of copy/paste errors, and the code is cleaner.
2595
 *
2596
 * This requires an object, not a pointer to an object, nor an array.
2597
 *
2598
 * \param x the object to clear.
2599
 *
2600
 * \threadsafety It is safe to call this macro from any thread.
2601
 *
2602
 * \since This macro is available since SDL 3.2.0.
2603
 *
2604
 * \sa SDL_zerop
2605
 * \sa SDL_zeroa
2606
 */
2607
2
#define SDL_zero(x) SDL_memset(&(x), 0, sizeof((x)))
2608
2609
/**
2610
 * Clear an object's memory to zero, using a pointer.
2611
 *
2612
 * This is wrapper over SDL_memset that handles calculating the object size,
2613
 * so there's no chance of copy/paste errors, and the code is cleaner.
2614
 *
2615
 * This requires a pointer to an object, not an object itself, nor an array.
2616
 *
2617
 * \param x a pointer to the object to clear.
2618
 *
2619
 * \threadsafety It is safe to call this macro from any thread.
2620
 *
2621
 * \since This macro is available since SDL 3.2.0.
2622
 *
2623
 * \sa SDL_zero
2624
 * \sa SDL_zeroa
2625
 */
2626
#define SDL_zerop(x) SDL_memset((x), 0, sizeof(*(x)))
2627
2628
/**
2629
 * Clear an array's memory to zero.
2630
 *
2631
 * This is wrapper over SDL_memset that handles calculating the array size, so
2632
 * there's no chance of copy/paste errors, and the code is cleaner.
2633
 *
2634
 * This requires an array, not an object, nor a pointer to an object.
2635
 *
2636
 * \param x an array to clear.
2637
 *
2638
 * \threadsafety It is safe to call this macro from any thread.
2639
 *
2640
 * \since This macro is available since SDL 3.2.0.
2641
 *
2642
 * \sa SDL_zero
2643
 * \sa SDL_zeroa
2644
 */
2645
#define SDL_zeroa(x) SDL_memset((x), 0, sizeof((x)))
2646
2647
2648
/**
2649
 * Compare two buffers of memory.
2650
 *
2651
 * \param s1 the first buffer to compare. NULL is not permitted!
2652
 * \param s2 the second buffer to compare. NULL is not permitted!
2653
 * \param len the number of bytes to compare between the buffers.
2654
 * \returns less than zero if s1 is "less than" s2, greater than zero if s1 is
2655
 *          "greater than" s2, and zero if the buffers match exactly for `len`
2656
 *          bytes.
2657
 *
2658
 * \threadsafety It is safe to call this function from any thread.
2659
 *
2660
 * \since This function is available since SDL 3.2.0.
2661
 */
2662
extern SDL_DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len);
2663
2664
/**
2665
 * This works exactly like wcslen() but doesn't require access to a C runtime.
2666
 *
2667
 * Counts the number of wchar_t values in `wstr`, excluding the null
2668
 * terminator.
2669
 *
2670
 * Like SDL_strlen only counts bytes and not codepoints in a UTF-8 string,
2671
 * this counts wchar_t values in a string, even if the string's encoding is of
2672
 * variable width, like UTF-16.
2673
 *
2674
 * Also be aware that wchar_t is different sizes on different platforms (4
2675
 * bytes on Linux, 2 on Windows, etc).
2676
 *
2677
 * \param wstr The null-terminated wide string to read. Must not be NULL.
2678
 * \returns the length (in wchar_t values, excluding the null terminator) of
2679
 *          `wstr`.
2680
 *
2681
 * \threadsafety It is safe to call this function from any thread.
2682
 *
2683
 * \since This function is available since SDL 3.2.0.
2684
 *
2685
 * \sa SDL_wcsnlen
2686
 * \sa SDL_utf8strlen
2687
 * \sa SDL_utf8strnlen
2688
 */
2689
extern SDL_DECLSPEC size_t SDLCALL SDL_wcslen(const wchar_t *wstr);
2690
2691
/**
2692
 * This works exactly like wcsnlen() but doesn't require access to a C
2693
 * runtime.
2694
 *
2695
 * Counts up to a maximum of `maxlen` wchar_t values in `wstr`, excluding the
2696
 * null terminator.
2697
 *
2698
 * Like SDL_strnlen only counts bytes and not codepoints in a UTF-8 string,
2699
 * this counts wchar_t values in a string, even if the string's encoding is of
2700
 * variable width, like UTF-16.
2701
 *
2702
 * Also be aware that wchar_t is different sizes on different platforms (4
2703
 * bytes on Linux, 2 on Windows, etc).
2704
 *
2705
 * Also, `maxlen` is a count of wide characters, not bytes!
2706
 *
2707
 * \param wstr The null-terminated wide string to read. Must not be NULL.
2708
 * \param maxlen The maximum amount of wide characters to count.
2709
 * \returns the length (in wide characters, excluding the null terminator) of
2710
 *          `wstr` but never more than `maxlen`.
2711
 *
2712
 * \threadsafety It is safe to call this function from any thread.
2713
 *
2714
 * \since This function is available since SDL 3.2.0.
2715
 *
2716
 * \sa SDL_wcslen
2717
 * \sa SDL_utf8strlen
2718
 * \sa SDL_utf8strnlen
2719
 */
2720
extern SDL_DECLSPEC size_t SDLCALL SDL_wcsnlen(const wchar_t *wstr, size_t maxlen);
2721
2722
/**
2723
 * Copy a wide string.
2724
 *
2725
 * This function copies `maxlen` - 1 wide characters from `src` to `dst`, then
2726
 * appends a null terminator.
2727
 *
2728
 * `src` and `dst` must not overlap.
2729
 *
2730
 * If `maxlen` is 0, no wide characters are copied and no null terminator is
2731
 * written.
2732
 *
2733
 * \param dst The destination buffer. Must not be NULL, and must not overlap
2734
 *            with `src`.
2735
 * \param src The null-terminated wide string to copy. Must not be NULL, and
2736
 *            must not overlap with `dst`.
2737
 * \param maxlen The length (in wide characters) of the destination buffer.
2738
 * \returns the length (in wide characters, excluding the null terminator) of
2739
 *          `src`.
2740
 *
2741
 * \threadsafety It is safe to call this function from any thread.
2742
 *
2743
 * \since This function is available since SDL 3.2.0.
2744
 *
2745
 * \sa SDL_wcslcat
2746
 */
2747
extern SDL_DECLSPEC size_t SDLCALL SDL_wcslcpy(SDL_OUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
2748
2749
/**
2750
 * Concatenate wide strings.
2751
 *
2752
 * This function appends up to `maxlen` - SDL_wcslen(dst) - 1 wide characters
2753
 * from `src` to the end of the wide string in `dst`, then appends a null
2754
 * terminator.
2755
 *
2756
 * `src` and `dst` must not overlap.
2757
 *
2758
 * If `maxlen` - SDL_wcslen(dst) - 1 is less than or equal to 0, then `dst` is
2759
 * unmodified.
2760
 *
2761
 * \param dst The destination buffer already containing the first
2762
 *            null-terminated wide string. Must not be NULL and must not
2763
 *            overlap with `src`.
2764
 * \param src The second null-terminated wide string. Must not be NULL, and
2765
 *            must not overlap with `dst`.
2766
 * \param maxlen The length (in wide characters) of the destination buffer.
2767
 * \returns the length (in wide characters, excluding the null terminator) of
2768
 *          the string in `dst` plus the length of `src`.
2769
 *
2770
 * \threadsafety It is safe to call this function from any thread.
2771
 *
2772
 * \since This function is available since SDL 3.2.0.
2773
 *
2774
 * \sa SDL_wcslcpy
2775
 */
2776
extern SDL_DECLSPEC size_t SDLCALL SDL_wcslcat(SDL_INOUT_Z_CAP(maxlen) wchar_t *dst, const wchar_t *src, size_t maxlen);
2777
2778
/**
2779
 * Allocate a copy of a wide string.
2780
 *
2781
 * This allocates enough space for a null-terminated copy of `wstr`, using
2782
 * SDL_malloc, and then makes a copy of the string into this space.
2783
 *
2784
 * The returned string is owned by the caller, and should be passed to
2785
 * SDL_free when no longer needed.
2786
 *
2787
 * \param wstr the string to copy.
2788
 * \returns a pointer to the newly-allocated wide string.
2789
 *
2790
 * \threadsafety It is safe to call this function from any thread.
2791
 *
2792
 * \since This function is available since SDL 3.2.0.
2793
 */
2794
extern SDL_DECLSPEC wchar_t * SDLCALL SDL_wcsdup(const wchar_t *wstr);
2795
2796
/**
2797
 * Search a wide string for the first instance of a specific substring.
2798
 *
2799
 * The search ends once it finds the requested substring, or a null terminator
2800
 * byte to end the string.
2801
 *
2802
 * Note that this looks for strings of _wide characters_, not _codepoints_, so
2803
 * it's legal to search for malformed and incomplete UTF-16 sequences.
2804
 *
2805
 * \param haystack the wide string to search. Must not be NULL.
2806
 * \param needle the wide string to search for. Must not be NULL.
2807
 * \returns a pointer to the first instance of `needle` in the string, or NULL
2808
 *          if not found.
2809
 *
2810
 * \threadsafety It is safe to call this function from any thread.
2811
 *
2812
 * \since This function is available since SDL 3.2.0.
2813
 */
2814
extern SDL_DECLSPEC wchar_t * SDLCALL SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle);
2815
2816
/**
2817
 * Search a wide string, up to n wide chars, for the first instance of a
2818
 * specific substring.
2819
 *
2820
 * The search ends once it finds the requested substring, or a null terminator
2821
 * value to end the string, or `maxlen` wide character have been examined. It
2822
 * is possible to use this function on a wide string without a null
2823
 * terminator.
2824
 *
2825
 * Note that this looks for strings of _wide characters_, not _codepoints_, so
2826
 * it's legal to search for malformed and incomplete UTF-16 sequences.
2827
 *
2828
 * \param haystack the wide string to search. Must not be NULL.
2829
 * \param needle the wide string to search for. Must not be NULL.
2830
 * \param maxlen the maximum number of wide characters to search in
2831
 *               `haystack`.
2832
 * \returns a pointer to the first instance of `needle` in the string, or NULL
2833
 *          if not found.
2834
 *
2835
 * \threadsafety It is safe to call this function from any thread.
2836
 *
2837
 * \since This function is available since SDL 3.2.0.
2838
 */
2839
extern SDL_DECLSPEC wchar_t * SDLCALL SDL_wcsnstr(const wchar_t *haystack, const wchar_t *needle, size_t maxlen);
2840
2841
/**
2842
 * Compare two null-terminated wide strings.
2843
 *
2844
 * This only compares wchar_t values until it hits a null-terminating
2845
 * character; it does not care if the string is well-formed UTF-16 (or UTF-32,
2846
 * depending on your platform's wchar_t size), or uses valid Unicode values.
2847
 *
2848
 * \param str1 the first string to compare. NULL is not permitted!
2849
 * \param str2 the second string to compare. NULL is not permitted!
2850
 * \returns less than zero if str1 is "less than" str2, greater than zero if
2851
 *          str1 is "greater than" str2, and zero if the strings match
2852
 *          exactly.
2853
 *
2854
 * \threadsafety It is safe to call this function from any thread.
2855
 *
2856
 * \since This function is available since SDL 3.2.0.
2857
 */
2858
extern SDL_DECLSPEC int SDLCALL SDL_wcscmp(const wchar_t *str1, const wchar_t *str2);
2859
2860
/**
2861
 * Compare two wide strings up to a number of wchar_t values.
2862
 *
2863
 * This only compares wchar_t values; it does not care if the string is
2864
 * well-formed UTF-16 (or UTF-32, depending on your platform's wchar_t size),
2865
 * or uses valid Unicode values.
2866
 *
2867
 * Note that while this function is intended to be used with UTF-16 (or
2868
 * UTF-32, depending on your platform's definition of wchar_t), it is
2869
 * comparing raw wchar_t values and not Unicode codepoints: `maxlen` specifies
2870
 * a wchar_t limit! If the limit lands in the middle of a multi-wchar UTF-16
2871
 * sequence, it will only compare a portion of the final character.
2872
 *
2873
 * `maxlen` specifies a maximum number of wchar_t to compare; if the strings
2874
 * match to this number of wide chars (or both have matched to a
2875
 * null-terminator character before this count), they will be considered
2876
 * equal.
2877
 *
2878
 * \param str1 the first string to compare. NULL is not permitted!
2879
 * \param str2 the second string to compare. NULL is not permitted!
2880
 * \param maxlen the maximum number of wchar_t to compare.
2881
 * \returns less than zero if str1 is "less than" str2, greater than zero if
2882
 *          str1 is "greater than" str2, and zero if the strings match
2883
 *          exactly.
2884
 *
2885
 * \threadsafety It is safe to call this function from any thread.
2886
 *
2887
 * \since This function is available since SDL 3.2.0.
2888
 */
2889
extern SDL_DECLSPEC int SDLCALL SDL_wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen);
2890
2891
/**
2892
 * Compare two null-terminated wide strings, case-insensitively.
2893
 *
2894
 * This will work with Unicode strings, using a technique called
2895
 * "case-folding" to handle the vast majority of case-sensitive human
2896
 * languages regardless of system locale. It can deal with expanding values: a
2897
 * German Eszett character can compare against two ASCII 's' chars and be
2898
 * considered a match, for example. A notable exception: it does not handle
2899
 * the Turkish 'i' character; human language is complicated!
2900
 *
2901
 * Depending on your platform, "wchar_t" might be 2 bytes, and expected to be
2902
 * UTF-16 encoded (like Windows), or 4 bytes in UTF-32 format. Since this
2903
 * handles Unicode, it expects the string to be well-formed and not a
2904
 * null-terminated string of arbitrary bytes. Characters that are not valid
2905
 * UTF-16 (or UTF-32) are treated as Unicode character U+FFFD (REPLACEMENT
2906
 * CHARACTER), which is to say two strings of random bits may turn out to
2907
 * match if they convert to the same amount of replacement characters.
2908
 *
2909
 * \param str1 the first string to compare. NULL is not permitted!
2910
 * \param str2 the second string to compare. NULL is not permitted!
2911
 * \returns less than zero if str1 is "less than" str2, greater than zero if
2912
 *          str1 is "greater than" str2, and zero if the strings match
2913
 *          exactly.
2914
 *
2915
 * \threadsafety It is safe to call this function from any thread.
2916
 *
2917
 * \since This function is available since SDL 3.2.0.
2918
 */
2919
extern SDL_DECLSPEC int SDLCALL SDL_wcscasecmp(const wchar_t *str1, const wchar_t *str2);
2920
2921
/**
2922
 * Compare two wide strings, case-insensitively, up to a number of wchar_t.
2923
 *
2924
 * This will work with Unicode strings, using a technique called
2925
 * "case-folding" to handle the vast majority of case-sensitive human
2926
 * languages regardless of system locale. It can deal with expanding values: a
2927
 * German Eszett character can compare against two ASCII 's' chars and be
2928
 * considered a match, for example. A notable exception: it does not handle
2929
 * the Turkish 'i' character; human language is complicated!
2930
 *
2931
 * Depending on your platform, "wchar_t" might be 2 bytes, and expected to be
2932
 * UTF-16 encoded (like Windows), or 4 bytes in UTF-32 format. Since this
2933
 * handles Unicode, it expects the string to be well-formed and not a
2934
 * null-terminated string of arbitrary bytes. Characters that are not valid
2935
 * UTF-16 (or UTF-32) are treated as Unicode character U+FFFD (REPLACEMENT
2936
 * CHARACTER), which is to say two strings of random bits may turn out to
2937
 * match if they convert to the same amount of replacement characters.
2938
 *
2939
 * Note that while this function might deal with variable-sized characters,
2940
 * `maxlen` specifies a _wchar_ limit! If the limit lands in the middle of a
2941
 * multi-byte UTF-16 sequence, it may convert a portion of the final character
2942
 * to one or more Unicode character U+FFFD (REPLACEMENT CHARACTER) so as not
2943
 * to overflow a buffer.
2944
 *
2945
 * `maxlen` specifies a maximum number of wchar_t values to compare; if the
2946
 * strings match to this number of wchar_t (or both have matched to a
2947
 * null-terminator character before this number of bytes), they will be
2948
 * considered equal.
2949
 *
2950
 * \param str1 the first string to compare. NULL is not permitted!
2951
 * \param str2 the second string to compare. NULL is not permitted!
2952
 * \param maxlen the maximum number of wchar_t values to compare.
2953
 * \returns less than zero if str1 is "less than" str2, greater than zero if
2954
 *          str1 is "greater than" str2, and zero if the strings match
2955
 *          exactly.
2956
 *
2957
 * \threadsafety It is safe to call this function from any thread.
2958
 *
2959
 * \since This function is available since SDL 3.2.0.
2960
 */
2961
extern SDL_DECLSPEC int SDLCALL SDL_wcsncasecmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen);
2962
2963
/**
2964
 * Parse a `long` from a wide string.
2965
 *
2966
 * If `str` starts with whitespace, then those whitespace characters are
2967
 * skipped before attempting to parse the number.
2968
 *
2969
 * If the parsed number does not fit inside a `long`, the result is clamped to
2970
 * the minimum and maximum representable `long` values.
2971
 *
2972
 * \param str The null-terminated wide string to read. Must not be NULL.
2973
 * \param endp If not NULL, the address of the first invalid wide character
2974
 *             (i.e. the next character after the parsed number) will be
2975
 *             written to this pointer.
2976
 * \param base The base of the integer to read. Supported values are 0 and 2
2977
 *             to 36 inclusive. If 0, the base will be inferred from the
2978
 *             number's prefix (0x for hexadecimal, 0 for octal, decimal
2979
 *             otherwise).
2980
 * \returns the parsed `long`, or 0 if no number could be parsed.
2981
 *
2982
 * \threadsafety It is safe to call this function from any thread.
2983
 *
2984
 * \since This function is available since SDL 3.2.0.
2985
 *
2986
 * \sa SDL_strtol
2987
 */
2988
extern SDL_DECLSPEC long SDLCALL SDL_wcstol(const wchar_t *str, wchar_t **endp, int base);
2989
2990
/**
2991
 * This works exactly like strlen() but doesn't require access to a C runtime.
2992
 *
2993
 * Counts the bytes in `str`, excluding the null terminator.
2994
 *
2995
 * If you need the length of a UTF-8 string, consider using SDL_utf8strlen().
2996
 *
2997
 * \param str The null-terminated string to read. Must not be NULL.
2998
 * \returns the length (in bytes, excluding the null terminator) of `src`.
2999
 *
3000
 * \threadsafety It is safe to call this function from any thread.
3001
 *
3002
 * \since This function is available since SDL 3.2.0.
3003
 *
3004
 * \sa SDL_strnlen
3005
 * \sa SDL_utf8strlen
3006
 * \sa SDL_utf8strnlen
3007
 */
3008
extern SDL_DECLSPEC size_t SDLCALL SDL_strlen(const char *str);
3009
3010
/**
3011
 * This works exactly like strnlen() but doesn't require access to a C
3012
 * runtime.
3013
 *
3014
 * Counts up to a maximum of `maxlen` bytes in `str`, excluding the null
3015
 * terminator.
3016
 *
3017
 * If you need the length of a UTF-8 string, consider using SDL_utf8strnlen().
3018
 *
3019
 * \param str The null-terminated string to read. Must not be NULL.
3020
 * \param maxlen The maximum amount of bytes to count.
3021
 * \returns the length (in bytes, excluding the null terminator) of `src` but
3022
 *          never more than `maxlen`.
3023
 *
3024
 * \threadsafety It is safe to call this function from any thread.
3025
 *
3026
 * \since This function is available since SDL 3.2.0.
3027
 *
3028
 * \sa SDL_strlen
3029
 * \sa SDL_utf8strlen
3030
 * \sa SDL_utf8strnlen
3031
 */
3032
extern SDL_DECLSPEC size_t SDLCALL SDL_strnlen(const char *str, size_t maxlen);
3033
3034
/**
3035
 * Copy a string.
3036
 *
3037
 * This function copies up to `maxlen` - 1 characters from `src` to `dst`,
3038
 * then appends a null terminator.
3039
 *
3040
 * If `maxlen` is 0, no characters are copied and no null terminator is
3041
 * written.
3042
 *
3043
 * If you want to copy an UTF-8 string but need to ensure that multi-byte
3044
 * sequences are not truncated, consider using SDL_utf8strlcpy().
3045
 *
3046
 * \param dst The destination buffer. Must not be NULL, and must not overlap
3047
 *            with `src`.
3048
 * \param src The null-terminated string to copy. Must not be NULL, and must
3049
 *            not overlap with `dst`.
3050
 * \param maxlen The length (in characters) of the destination buffer.
3051
 * \returns the length (in characters, excluding the null terminator) of
3052
 *          `src`.
3053
 *
3054
 * \threadsafety It is safe to call this function from any thread.
3055
 *
3056
 * \since This function is available since SDL 3.2.0.
3057
 *
3058
 * \sa SDL_strlcat
3059
 * \sa SDL_utf8strlcpy
3060
 */
3061
extern SDL_DECLSPEC size_t SDLCALL SDL_strlcpy(SDL_OUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen);
3062
3063
/**
3064
 * Copy an UTF-8 string.
3065
 *
3066
 * This function copies up to `dst_bytes` - 1 bytes from `src` to `dst` while
3067
 * also ensuring that the string written to `dst` does not end in a truncated
3068
 * multi-byte sequence. Finally, it appends a null terminator.
3069
 *
3070
 * `src` and `dst` must not overlap.
3071
 *
3072
 * Note that unlike SDL_strlcpy(), this function returns the number of bytes
3073
 * written, not the length of `src`.
3074
 *
3075
 * \param dst The destination buffer. Must not be NULL, and must not overlap
3076
 *            with `src`.
3077
 * \param src The null-terminated UTF-8 string to copy. Must not be NULL, and
3078
 *            must not overlap with `dst`.
3079
 * \param dst_bytes The length (in bytes) of the destination buffer. Must not
3080
 *                  be 0.
3081
 * \returns the number of bytes written, excluding the null terminator.
3082
 *
3083
 * \threadsafety It is safe to call this function from any thread.
3084
 *
3085
 * \since This function is available since SDL 3.2.0.
3086
 *
3087
 * \sa SDL_strlcpy
3088
 */
3089
extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strlcpy(SDL_OUT_Z_CAP(dst_bytes) char *dst, const char *src, size_t dst_bytes);
3090
3091
/**
3092
 * Concatenate strings.
3093
 *
3094
 * This function appends up to `maxlen` - SDL_strlen(dst) - 1 characters from
3095
 * `src` to the end of the string in `dst`, then appends a null terminator.
3096
 *
3097
 * `src` and `dst` must not overlap.
3098
 *
3099
 * If `maxlen` - SDL_strlen(dst) - 1 is less than or equal to 0, then `dst` is
3100
 * unmodified.
3101
 *
3102
 * \param dst The destination buffer already containing the first
3103
 *            null-terminated string. Must not be NULL and must not overlap
3104
 *            with `src`.
3105
 * \param src The second null-terminated string. Must not be NULL, and must
3106
 *            not overlap with `dst`.
3107
 * \param maxlen The length (in characters) of the destination buffer.
3108
 * \returns the length (in characters, excluding the null terminator) of the
3109
 *          string in `dst` plus the length of `src`.
3110
 *
3111
 * \threadsafety It is safe to call this function from any thread.
3112
 *
3113
 * \since This function is available since SDL 3.2.0.
3114
 *
3115
 * \sa SDL_strlcpy
3116
 */
3117
extern SDL_DECLSPEC size_t SDLCALL SDL_strlcat(SDL_INOUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen);
3118
3119
/**
3120
 * Allocate a copy of a string.
3121
 *
3122
 * This allocates enough space for a null-terminated copy of `str`, using
3123
 * SDL_malloc, and then makes a copy of the string into this space.
3124
 *
3125
 * The returned string is owned by the caller, and should be passed to
3126
 * SDL_free when no longer needed.
3127
 *
3128
 * \param str the string to copy.
3129
 * \returns a pointer to the newly-allocated string.
3130
 *
3131
 * \threadsafety It is safe to call this function from any thread.
3132
 *
3133
 * \since This function is available since SDL 3.2.0.
3134
 */
3135
extern SDL_DECLSPEC SDL_MALLOC char * SDLCALL SDL_strdup(const char *str);
3136
3137
/**
3138
 * Allocate a copy of a string, up to n characters.
3139
 *
3140
 * This allocates enough space for a null-terminated copy of `str`, up to
3141
 * `maxlen` bytes, using SDL_malloc, and then makes a copy of the string into
3142
 * this space.
3143
 *
3144
 * If the string is longer than `maxlen` bytes, the returned string will be
3145
 * `maxlen` bytes long, plus a null-terminator character that isn't included
3146
 * in the count.
3147
 *
3148
 * The returned string is owned by the caller, and should be passed to
3149
 * SDL_free when no longer needed.
3150
 *
3151
 * \param str the string to copy.
3152
 * \param maxlen the maximum length of the copied string, not counting the
3153
 *               null-terminator character.
3154
 * \returns a pointer to the newly-allocated string.
3155
 *
3156
 * \threadsafety It is safe to call this function from any thread.
3157
 *
3158
 * \since This function is available since SDL 3.2.0.
3159
 */
3160
extern SDL_DECLSPEC SDL_MALLOC char * SDLCALL SDL_strndup(const char *str, size_t maxlen);
3161
3162
/**
3163
 * Reverse a string's contents.
3164
 *
3165
 * This reverses a null-terminated string in-place. Only the content of the
3166
 * string is reversed; the null-terminator character remains at the end of the
3167
 * reversed string.
3168
 *
3169
 * **WARNING**: This function reverses the _bytes_ of the string, not the
3170
 * codepoints. If `str` is a UTF-8 string with Unicode codepoints > 127, this
3171
 * will ruin the string data. You should only use this function on strings
3172
 * that are completely comprised of low ASCII characters.
3173
 *
3174
 * \param str the string to reverse.
3175
 * \returns `str`.
3176
 *
3177
 * \threadsafety It is safe to call this function from any thread.
3178
 *
3179
 * \since This function is available since SDL 3.2.0.
3180
 */
3181
extern SDL_DECLSPEC char * SDLCALL SDL_strrev(char *str);
3182
3183
/**
3184
 * Convert a string to uppercase.
3185
 *
3186
 * **WARNING**: Regardless of system locale, this will only convert ASCII
3187
 * values 'A' through 'Z' to uppercase.
3188
 *
3189
 * This function operates on a null-terminated string of bytes--even if it is
3190
 * malformed UTF-8!--and converts ASCII characters 'a' through 'z' to their
3191
 * uppercase equivalents in-place, returning the original `str` pointer.
3192
 *
3193
 * \param str the string to convert in-place. Can not be NULL.
3194
 * \returns the `str` pointer passed into this function.
3195
 *
3196
 * \threadsafety It is safe to call this function from any thread.
3197
 *
3198
 * \since This function is available since SDL 3.2.0.
3199
 *
3200
 * \sa SDL_strlwr
3201
 */
3202
extern SDL_DECLSPEC char * SDLCALL SDL_strupr(char *str);
3203
3204
/**
3205
 * Convert a string to lowercase.
3206
 *
3207
 * **WARNING**: Regardless of system locale, this will only convert ASCII
3208
 * values 'A' through 'Z' to lowercase.
3209
 *
3210
 * This function operates on a null-terminated string of bytes--even if it is
3211
 * malformed UTF-8!--and converts ASCII characters 'A' through 'Z' to their
3212
 * lowercase equivalents in-place, returning the original `str` pointer.
3213
 *
3214
 * \param str the string to convert in-place. Can not be NULL.
3215
 * \returns the `str` pointer passed into this function.
3216
 *
3217
 * \threadsafety It is safe to call this function from any thread.
3218
 *
3219
 * \since This function is available since SDL 3.2.0.
3220
 *
3221
 * \sa SDL_strupr
3222
 */
3223
extern SDL_DECLSPEC char * SDLCALL SDL_strlwr(char *str);
3224
3225
/**
3226
 * Search a string for the first instance of a specific byte.
3227
 *
3228
 * The search ends once it finds the requested byte value, or a null
3229
 * terminator byte to end the string.
3230
 *
3231
 * Note that this looks for _bytes_, not _characters_, so you cannot match
3232
 * against a Unicode codepoint > 255, regardless of character encoding.
3233
 *
3234
 * \param str the string to search. Must not be NULL.
3235
 * \param c the byte value to search for.
3236
 * \returns a pointer to the first instance of `c` in the string, or NULL if
3237
 *          not found.
3238
 *
3239
 * \threadsafety It is safe to call this function from any thread.
3240
 *
3241
 * \since This function is available since SDL 3.2.0.
3242
 */
3243
extern SDL_DECLSPEC char * SDLCALL SDL_strchr(const char *str, int c);
3244
3245
/**
3246
 * Search a string for the last instance of a specific byte.
3247
 *
3248
 * The search must go until it finds a null terminator byte to end the string.
3249
 *
3250
 * Note that this looks for _bytes_, not _characters_, so you cannot match
3251
 * against a Unicode codepoint > 255, regardless of character encoding.
3252
 *
3253
 * \param str the string to search. Must not be NULL.
3254
 * \param c the byte value to search for.
3255
 * \returns a pointer to the last instance of `c` in the string, or NULL if
3256
 *          not found.
3257
 *
3258
 * \threadsafety It is safe to call this function from any thread.
3259
 *
3260
 * \since This function is available since SDL 3.2.0.
3261
 */
3262
extern SDL_DECLSPEC char * SDLCALL SDL_strrchr(const char *str, int c);
3263
3264
/**
3265
 * Search a string for the first instance of a specific substring.
3266
 *
3267
 * The search ends once it finds the requested substring, or a null terminator
3268
 * byte to end the string.
3269
 *
3270
 * Note that this looks for strings of _bytes_, not _characters_, so it's
3271
 * legal to search for malformed and incomplete UTF-8 sequences.
3272
 *
3273
 * \param haystack the string to search. Must not be NULL.
3274
 * \param needle the string to search for. Must not be NULL.
3275
 * \returns a pointer to the first instance of `needle` in the string, or NULL
3276
 *          if not found.
3277
 *
3278
 * \threadsafety It is safe to call this function from any thread.
3279
 *
3280
 * \since This function is available since SDL 3.2.0.
3281
 */
3282
extern SDL_DECLSPEC char * SDLCALL SDL_strstr(const char *haystack, const char *needle);
3283
3284
/**
3285
 * Search a string, up to n bytes, for the first instance of a specific
3286
 * substring.
3287
 *
3288
 * The search ends once it finds the requested substring, or a null terminator
3289
 * byte to end the string, or `maxlen` bytes have been examined. It is
3290
 * possible to use this function on a string without a null terminator.
3291
 *
3292
 * Note that this looks for strings of _bytes_, not _characters_, so it's
3293
 * legal to search for malformed and incomplete UTF-8 sequences.
3294
 *
3295
 * \param haystack the string to search. Must not be NULL.
3296
 * \param needle the string to search for. Must not be NULL.
3297
 * \param maxlen the maximum number of bytes to search in `haystack`.
3298
 * \returns a pointer to the first instance of `needle` in the string, or NULL
3299
 *          if not found.
3300
 *
3301
 * \threadsafety It is safe to call this function from any thread.
3302
 *
3303
 * \since This function is available since SDL 3.2.0.
3304
 */
3305
extern SDL_DECLSPEC char * SDLCALL SDL_strnstr(const char *haystack, const char *needle, size_t maxlen);
3306
3307
/**
3308
 * Search a UTF-8 string for the first instance of a specific substring,
3309
 * case-insensitively.
3310
 *
3311
 * This will work with Unicode strings, using a technique called
3312
 * "case-folding" to handle the vast majority of case-sensitive human
3313
 * languages regardless of system locale. It can deal with expanding values: a
3314
 * German Eszett character can compare against two ASCII 's' chars and be
3315
 * considered a match, for example. A notable exception: it does not handle
3316
 * the Turkish 'i' character; human language is complicated!
3317
 *
3318
 * Since this handles Unicode, it expects the strings to be well-formed UTF-8
3319
 * and not a null-terminated string of arbitrary bytes. Bytes that are not
3320
 * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT
3321
 * CHARACTER), which is to say two strings of random bits may turn out to
3322
 * match if they convert to the same amount of replacement characters.
3323
 *
3324
 * \param haystack the string to search. Must not be NULL.
3325
 * \param needle the string to search for. Must not be NULL.
3326
 * \returns a pointer to the first instance of `needle` in the string, or NULL
3327
 *          if not found.
3328
 *
3329
 * \threadsafety It is safe to call this function from any thread.
3330
 *
3331
 * \since This function is available since SDL 3.2.0.
3332
 */
3333
extern SDL_DECLSPEC char * SDLCALL SDL_strcasestr(const char *haystack, const char *needle);
3334
3335
/**
3336
 * This works exactly like strtok_r() but doesn't require access to a C
3337
 * runtime.
3338
 *
3339
 * Break a string up into a series of tokens.
3340
 *
3341
 * To start tokenizing a new string, `str` should be the non-NULL address of
3342
 * the string to start tokenizing. Future calls to get the next token from the
3343
 * same string should specify a NULL.
3344
 *
3345
 * Note that this function will overwrite pieces of `str` with null chars to
3346
 * split it into tokens. This function cannot be used with const/read-only
3347
 * strings!
3348
 *
3349
 * `saveptr` just needs to point to a `char *` that can be overwritten; SDL
3350
 * will use this to save tokenizing state between calls. It is initialized if
3351
 * `str` is non-NULL, and used to resume tokenizing when `str` is NULL.
3352
 *
3353
 * \param str the string to tokenize, or NULL to continue tokenizing.
3354
 * \param delim the delimiter string that separates tokens.
3355
 * \param saveptr pointer to a char *, used for ongoing state.
3356
 * \returns A pointer to the next token, or NULL if no tokens remain.
3357
 *
3358
 * \threadsafety It is safe to call this function from any thread.
3359
 *
3360
 * \since This function is available since SDL 3.2.0.
3361
 */
3362
extern SDL_DECLSPEC char * SDLCALL SDL_strtok_r(char *str, const char *delim, char **saveptr);
3363
3364
/**
3365
 * Count the number of codepoints in a UTF-8 string.
3366
 *
3367
 * Counts the _codepoints_, not _bytes_, in `str`, excluding the null
3368
 * terminator.
3369
 *
3370
 * If you need to count the bytes in a string instead, consider using
3371
 * SDL_strlen().
3372
 *
3373
 * Since this handles Unicode, it expects the strings to be well-formed UTF-8
3374
 * and not a null-terminated string of arbitrary bytes. Bytes that are not
3375
 * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT
3376
 * CHARACTER), so a malformed or incomplete UTF-8 sequence might increase the
3377
 * count by several replacement characters.
3378
 *
3379
 * \param str The null-terminated UTF-8 string to read. Must not be NULL.
3380
 * \returns The length (in codepoints, excluding the null terminator) of
3381
 *          `src`.
3382
 *
3383
 * \threadsafety It is safe to call this function from any thread.
3384
 *
3385
 * \since This function is available since SDL 3.2.0.
3386
 *
3387
 * \sa SDL_utf8strnlen
3388
 * \sa SDL_strlen
3389
 */
3390
extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strlen(const char *str);
3391
3392
/**
3393
 * Count the number of codepoints in a UTF-8 string, up to n bytes.
3394
 *
3395
 * Counts the _codepoints_, not _bytes_, in `str`, excluding the null
3396
 * terminator.
3397
 *
3398
 * If you need to count the bytes in a string instead, consider using
3399
 * SDL_strnlen().
3400
 *
3401
 * The counting stops at `bytes` bytes (not codepoints!). This seems
3402
 * counterintuitive, but makes it easy to express the total size of the
3403
 * string's buffer.
3404
 *
3405
 * Since this handles Unicode, it expects the strings to be well-formed UTF-8
3406
 * and not a null-terminated string of arbitrary bytes. Bytes that are not
3407
 * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT
3408
 * CHARACTER), so a malformed or incomplete UTF-8 sequence might increase the
3409
 * count by several replacement characters.
3410
 *
3411
 * \param str The null-terminated UTF-8 string to read. Must not be NULL.
3412
 * \param bytes The maximum amount of bytes to count.
3413
 * \returns The length (in codepoints, excluding the null terminator) of `src`
3414
 *          but never more than `maxlen`.
3415
 *
3416
 * \threadsafety It is safe to call this function from any thread.
3417
 *
3418
 * \since This function is available since SDL 3.2.0.
3419
 *
3420
 * \sa SDL_utf8strlen
3421
 * \sa SDL_strnlen
3422
 */
3423
extern SDL_DECLSPEC size_t SDLCALL SDL_utf8strnlen(const char *str, size_t bytes);
3424
3425
/**
3426
 * Convert an integer into a string.
3427
 *
3428
 * This requires a radix to specified for string format. Specifying 10
3429
 * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2
3430
 * to 36.
3431
 *
3432
 * Note that this function will overflow a buffer if `str` is not large enough
3433
 * to hold the output! It may be safer to use SDL_snprintf to clamp output, or
3434
 * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate
3435
 * much more space than you expect to use (and don't forget possible negative
3436
 * signs, null terminator bytes, etc).
3437
 *
3438
 * \param value the integer to convert.
3439
 * \param str the buffer to write the string into.
3440
 * \param radix the radix to use for string generation.
3441
 * \returns `str`.
3442
 *
3443
 * \threadsafety It is safe to call this function from any thread.
3444
 *
3445
 * \since This function is available since SDL 3.2.0.
3446
 *
3447
 * \sa SDL_uitoa
3448
 * \sa SDL_ltoa
3449
 * \sa SDL_lltoa
3450
 */
3451
extern SDL_DECLSPEC char * SDLCALL SDL_itoa(int value, char *str, int radix);
3452
3453
/**
3454
 * Convert an unsigned integer into a string.
3455
 *
3456
 * This requires a radix to specified for string format. Specifying 10
3457
 * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2
3458
 * to 36.
3459
 *
3460
 * Note that this function will overflow a buffer if `str` is not large enough
3461
 * to hold the output! It may be safer to use SDL_snprintf to clamp output, or
3462
 * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate
3463
 * much more space than you expect to use (and don't forget null terminator
3464
 * bytes, etc).
3465
 *
3466
 * \param value the unsigned integer to convert.
3467
 * \param str the buffer to write the string into.
3468
 * \param radix the radix to use for string generation.
3469
 * \returns `str`.
3470
 *
3471
 * \threadsafety It is safe to call this function from any thread.
3472
 *
3473
 * \since This function is available since SDL 3.2.0.
3474
 *
3475
 * \sa SDL_itoa
3476
 * \sa SDL_ultoa
3477
 * \sa SDL_ulltoa
3478
 */
3479
extern SDL_DECLSPEC char * SDLCALL SDL_uitoa(unsigned int value, char *str, int radix);
3480
3481
/**
3482
 * Convert a long integer into a string.
3483
 *
3484
 * This requires a radix to specified for string format. Specifying 10
3485
 * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2
3486
 * to 36.
3487
 *
3488
 * Note that this function will overflow a buffer if `str` is not large enough
3489
 * to hold the output! It may be safer to use SDL_snprintf to clamp output, or
3490
 * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate
3491
 * much more space than you expect to use (and don't forget possible negative
3492
 * signs, null terminator bytes, etc).
3493
 *
3494
 * \param value the long integer to convert.
3495
 * \param str the buffer to write the string into.
3496
 * \param radix the radix to use for string generation.
3497
 * \returns `str`.
3498
 *
3499
 * \threadsafety It is safe to call this function from any thread.
3500
 *
3501
 * \since This function is available since SDL 3.2.0.
3502
 *
3503
 * \sa SDL_ultoa
3504
 * \sa SDL_itoa
3505
 * \sa SDL_lltoa
3506
 */
3507
extern SDL_DECLSPEC char * SDLCALL SDL_ltoa(long value, char *str, int radix);
3508
3509
/**
3510
 * Convert an unsigned long integer into a string.
3511
 *
3512
 * This requires a radix to specified for string format. Specifying 10
3513
 * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2
3514
 * to 36.
3515
 *
3516
 * Note that this function will overflow a buffer if `str` is not large enough
3517
 * to hold the output! It may be safer to use SDL_snprintf to clamp output, or
3518
 * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate
3519
 * much more space than you expect to use (and don't forget null terminator
3520
 * bytes, etc).
3521
 *
3522
 * \param value the unsigned long integer to convert.
3523
 * \param str the buffer to write the string into.
3524
 * \param radix the radix to use for string generation.
3525
 * \returns `str`.
3526
 *
3527
 * \threadsafety It is safe to call this function from any thread.
3528
 *
3529
 * \since This function is available since SDL 3.2.0.
3530
 *
3531
 * \sa SDL_ltoa
3532
 * \sa SDL_uitoa
3533
 * \sa SDL_ulltoa
3534
 */
3535
extern SDL_DECLSPEC char * SDLCALL SDL_ultoa(unsigned long value, char *str, int radix);
3536
3537
#ifndef SDL_NOLONGLONG
3538
3539
/**
3540
 * Convert a long long integer into a string.
3541
 *
3542
 * This requires a radix to specified for string format. Specifying 10
3543
 * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2
3544
 * to 36.
3545
 *
3546
 * Note that this function will overflow a buffer if `str` is not large enough
3547
 * to hold the output! It may be safer to use SDL_snprintf to clamp output, or
3548
 * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate
3549
 * much more space than you expect to use (and don't forget possible negative
3550
 * signs, null terminator bytes, etc).
3551
 *
3552
 * \param value the long long integer to convert.
3553
 * \param str the buffer to write the string into.
3554
 * \param radix the radix to use for string generation.
3555
 * \returns `str`.
3556
 *
3557
 * \threadsafety It is safe to call this function from any thread.
3558
 *
3559
 * \since This function is available since SDL 3.2.0.
3560
 *
3561
 * \sa SDL_ulltoa
3562
 * \sa SDL_itoa
3563
 * \sa SDL_ltoa
3564
 */
3565
extern SDL_DECLSPEC char * SDLCALL SDL_lltoa(long long value, char *str, int radix);
3566
3567
/**
3568
 * Convert an unsigned long long integer into a string.
3569
 *
3570
 * This requires a radix to specified for string format. Specifying 10
3571
 * produces a decimal number, 16 hexadecimal, etc. Must be in the range of 2
3572
 * to 36.
3573
 *
3574
 * Note that this function will overflow a buffer if `str` is not large enough
3575
 * to hold the output! It may be safer to use SDL_snprintf to clamp output, or
3576
 * SDL_asprintf to allocate a buffer. Otherwise, it doesn't hurt to allocate
3577
 * much more space than you expect to use (and don't forget null terminator
3578
 * bytes, etc).
3579
 *
3580
 * \param value the unsigned long long integer to convert.
3581
 * \param str the buffer to write the string into.
3582
 * \param radix the radix to use for string generation.
3583
 * \returns `str`.
3584
 *
3585
 * \threadsafety It is safe to call this function from any thread.
3586
 *
3587
 * \since This function is available since SDL 3.2.0.
3588
 *
3589
 * \sa SDL_lltoa
3590
 * \sa SDL_uitoa
3591
 * \sa SDL_ultoa
3592
 */
3593
extern SDL_DECLSPEC char * SDLCALL SDL_ulltoa(unsigned long long value, char *str, int radix);
3594
#endif
3595
3596
/**
3597
 * Parse an `int` from a string.
3598
 *
3599
 * The result of calling `SDL_atoi(str)` is equivalent to
3600
 * `(int)SDL_strtol(str, NULL, 10)`.
3601
 *
3602
 * \param str The null-terminated string to read. Must not be NULL.
3603
 * \returns the parsed `int`.
3604
 *
3605
 * \threadsafety It is safe to call this function from any thread.
3606
 *
3607
 * \since This function is available since SDL 3.2.0.
3608
 *
3609
 * \sa SDL_atof
3610
 * \sa SDL_strtol
3611
 * \sa SDL_strtoul
3612
 * \sa SDL_strtoll
3613
 * \sa SDL_strtoull
3614
 * \sa SDL_strtod
3615
 * \sa SDL_itoa
3616
 */
3617
extern SDL_DECLSPEC int SDLCALL SDL_atoi(const char *str);
3618
3619
/**
3620
 * Parse a `double` from a string.
3621
 *
3622
 * The result of calling `SDL_atof(str)` is equivalent to `SDL_strtod(str,
3623
 * NULL)`.
3624
 *
3625
 * \param str The null-terminated string to read. Must not be NULL.
3626
 * \returns the parsed `double`.
3627
 *
3628
 * \threadsafety It is safe to call this function from any thread.
3629
 *
3630
 * \since This function is available since SDL 3.2.0.
3631
 *
3632
 * \sa SDL_atoi
3633
 * \sa SDL_strtol
3634
 * \sa SDL_strtoul
3635
 * \sa SDL_strtoll
3636
 * \sa SDL_strtoull
3637
 * \sa SDL_strtod
3638
 */
3639
extern SDL_DECLSPEC double SDLCALL SDL_atof(const char *str);
3640
3641
/**
3642
 * Parse a `long` from a string.
3643
 *
3644
 * If `str` starts with whitespace, then those whitespace characters are
3645
 * skipped before attempting to parse the number.
3646
 *
3647
 * If the parsed number does not fit inside a `long`, the result is clamped to
3648
 * the minimum and maximum representable `long` values.
3649
 *
3650
 * \param str The null-terminated string to read. Must not be NULL.
3651
 * \param endp If not NULL, the address of the first invalid character (i.e.
3652
 *             the next character after the parsed number) will be written to
3653
 *             this pointer.
3654
 * \param base The base of the integer to read. Supported values are 0 and 2
3655
 *             to 36 inclusive. If 0, the base will be inferred from the
3656
 *             number's prefix (0x for hexadecimal, 0 for octal, decimal
3657
 *             otherwise).
3658
 * \returns the parsed `long`, or 0 if no number could be parsed.
3659
 *
3660
 * \threadsafety It is safe to call this function from any thread.
3661
 *
3662
 * \since This function is available since SDL 3.2.0.
3663
 *
3664
 * \sa SDL_atoi
3665
 * \sa SDL_atof
3666
 * \sa SDL_strtoul
3667
 * \sa SDL_strtoll
3668
 * \sa SDL_strtoull
3669
 * \sa SDL_strtod
3670
 * \sa SDL_ltoa
3671
 * \sa SDL_wcstol
3672
 */
3673
extern SDL_DECLSPEC long SDLCALL SDL_strtol(const char *str, char **endp, int base);
3674
3675
/**
3676
 * Parse an `unsigned long` from a string.
3677
 *
3678
 * If `str` starts with whitespace, then those whitespace characters are
3679
 * skipped before attempting to parse the number.
3680
 *
3681
 * If the parsed number does not fit inside an `unsigned long`, the result is
3682
 * clamped to the maximum representable `unsigned long` value.
3683
 *
3684
 * \param str The null-terminated string to read. Must not be NULL.
3685
 * \param endp If not NULL, the address of the first invalid character (i.e.
3686
 *             the next character after the parsed number) will be written to
3687
 *             this pointer.
3688
 * \param base The base of the integer to read. Supported values are 0 and 2
3689
 *             to 36 inclusive. If 0, the base will be inferred from the
3690
 *             number's prefix (0x for hexadecimal, 0 for octal, decimal
3691
 *             otherwise).
3692
 * \returns the parsed `unsigned long`, or 0 if no number could be parsed.
3693
 *
3694
 * \threadsafety It is safe to call this function from any thread.
3695
 *
3696
 * \since This function is available since SDL 3.2.0.
3697
 *
3698
 * \sa SDL_atoi
3699
 * \sa SDL_atof
3700
 * \sa SDL_strtol
3701
 * \sa SDL_strtoll
3702
 * \sa SDL_strtoull
3703
 * \sa SDL_strtod
3704
 * \sa SDL_ultoa
3705
 */
3706
extern SDL_DECLSPEC unsigned long SDLCALL SDL_strtoul(const char *str, char **endp, int base);
3707
3708
#ifndef SDL_NOLONGLONG
3709
3710
/**
3711
 * Parse a `long long` from a string.
3712
 *
3713
 * If `str` starts with whitespace, then those whitespace characters are
3714
 * skipped before attempting to parse the number.
3715
 *
3716
 * If the parsed number does not fit inside a `long long`, the result is
3717
 * clamped to the minimum and maximum representable `long long` values.
3718
 *
3719
 * \param str The null-terminated string to read. Must not be NULL.
3720
 * \param endp If not NULL, the address of the first invalid character (i.e.
3721
 *             the next character after the parsed number) will be written to
3722
 *             this pointer.
3723
 * \param base The base of the integer to read. Supported values are 0 and 2
3724
 *             to 36 inclusive. If 0, the base will be inferred from the
3725
 *             number's prefix (0x for hexadecimal, 0 for octal, decimal
3726
 *             otherwise).
3727
 * \returns the parsed `long long`, or 0 if no number could be parsed.
3728
 *
3729
 * \threadsafety It is safe to call this function from any thread.
3730
 *
3731
 * \since This function is available since SDL 3.2.0.
3732
 *
3733
 * \sa SDL_atoi
3734
 * \sa SDL_atof
3735
 * \sa SDL_strtol
3736
 * \sa SDL_strtoul
3737
 * \sa SDL_strtoull
3738
 * \sa SDL_strtod
3739
 * \sa SDL_lltoa
3740
 */
3741
extern SDL_DECLSPEC long long SDLCALL SDL_strtoll(const char *str, char **endp, int base);
3742
3743
/**
3744
 * Parse an `unsigned long long` from a string.
3745
 *
3746
 * If `str` starts with whitespace, then those whitespace characters are
3747
 * skipped before attempting to parse the number.
3748
 *
3749
 * If the parsed number does not fit inside an `unsigned long long`, the
3750
 * result is clamped to the maximum representable `unsigned long long` value.
3751
 *
3752
 * \param str The null-terminated string to read. Must not be NULL.
3753
 * \param endp If not NULL, the address of the first invalid character (i.e.
3754
 *             the next character after the parsed number) will be written to
3755
 *             this pointer.
3756
 * \param base The base of the integer to read. Supported values are 0 and 2
3757
 *             to 36 inclusive. If 0, the base will be inferred from the
3758
 *             number's prefix (0x for hexadecimal, 0 for octal, decimal
3759
 *             otherwise).
3760
 * \returns the parsed `unsigned long long`, or 0 if no number could be
3761
 *          parsed.
3762
 *
3763
 * \threadsafety It is safe to call this function from any thread.
3764
 *
3765
 * \since This function is available since SDL 3.2.0.
3766
 *
3767
 * \sa SDL_atoi
3768
 * \sa SDL_atof
3769
 * \sa SDL_strtol
3770
 * \sa SDL_strtoll
3771
 * \sa SDL_strtoul
3772
 * \sa SDL_strtod
3773
 * \sa SDL_ulltoa
3774
 */
3775
extern SDL_DECLSPEC unsigned long long SDLCALL SDL_strtoull(const char *str, char **endp, int base);
3776
#endif
3777
3778
/**
3779
 * Parse a `double` from a string.
3780
 *
3781
 * This function makes fewer guarantees than the C runtime `strtod`:
3782
 *
3783
 * - Only decimal notation is guaranteed to be supported. The handling of
3784
 *   scientific and hexadecimal notation is unspecified.
3785
 * - Whether or not INF and NAN can be parsed is unspecified.
3786
 * - The precision of the result is unspecified.
3787
 *
3788
 * \param str the null-terminated string to read. Must not be NULL.
3789
 * \param endp if not NULL, the address of the first invalid character (i.e.
3790
 *             the next character after the parsed number) will be written to
3791
 *             this pointer.
3792
 * \returns the parsed `double`, or 0 if no number could be parsed.
3793
 *
3794
 * \threadsafety It is safe to call this function from any thread.
3795
 *
3796
 * \since This function is available since SDL 3.2.0.
3797
 *
3798
 * \sa SDL_atoi
3799
 * \sa SDL_atof
3800
 * \sa SDL_strtol
3801
 * \sa SDL_strtoll
3802
 * \sa SDL_strtoul
3803
 * \sa SDL_strtoull
3804
 */
3805
extern SDL_DECLSPEC double SDLCALL SDL_strtod(const char *str, char **endp);
3806
3807
/**
3808
 * Compare two null-terminated UTF-8 strings.
3809
 *
3810
 * Due to the nature of UTF-8 encoding, this will work with Unicode strings,
3811
 * since effectively this function just compares bytes until it hits a
3812
 * null-terminating character. Also due to the nature of UTF-8, this can be
3813
 * used with SDL_qsort() to put strings in (roughly) alphabetical order.
3814
 *
3815
 * \param str1 the first string to compare. NULL is not permitted!
3816
 * \param str2 the second string to compare. NULL is not permitted!
3817
 * \returns less than zero if str1 is "less than" str2, greater than zero if
3818
 *          str1 is "greater than" str2, and zero if the strings match
3819
 *          exactly.
3820
 *
3821
 * \threadsafety It is safe to call this function from any thread.
3822
 *
3823
 * \since This function is available since SDL 3.2.0.
3824
 */
3825
extern SDL_DECLSPEC int SDLCALL SDL_strcmp(const char *str1, const char *str2);
3826
3827
/**
3828
 * Compare two UTF-8 strings up to a number of bytes.
3829
 *
3830
 * Due to the nature of UTF-8 encoding, this will work with Unicode strings,
3831
 * since effectively this function just compares bytes until it hits a
3832
 * null-terminating character. Also due to the nature of UTF-8, this can be
3833
 * used with SDL_qsort() to put strings in (roughly) alphabetical order.
3834
 *
3835
 * Note that while this function is intended to be used with UTF-8, it is
3836
 * doing a bytewise comparison, and `maxlen` specifies a _byte_ limit! If the
3837
 * limit lands in the middle of a multi-byte UTF-8 sequence, it will only
3838
 * compare a portion of the final character.
3839
 *
3840
 * `maxlen` specifies a maximum number of bytes to compare; if the strings
3841
 * match to this number of bytes (or both have matched to a null-terminator
3842
 * character before this number of bytes), they will be considered equal.
3843
 *
3844
 * \param str1 the first string to compare. NULL is not permitted!
3845
 * \param str2 the second string to compare. NULL is not permitted!
3846
 * \param maxlen the maximum number of _bytes_ to compare.
3847
 * \returns less than zero if str1 is "less than" str2, greater than zero if
3848
 *          str1 is "greater than" str2, and zero if the strings match
3849
 *          exactly.
3850
 *
3851
 * \threadsafety It is safe to call this function from any thread.
3852
 *
3853
 * \since This function is available since SDL 3.2.0.
3854
 */
3855
extern SDL_DECLSPEC int SDLCALL SDL_strncmp(const char *str1, const char *str2, size_t maxlen);
3856
3857
/**
3858
 * Compare two null-terminated UTF-8 strings, case-insensitively.
3859
 *
3860
 * This will work with Unicode strings, using a technique called
3861
 * "case-folding" to handle the vast majority of case-sensitive human
3862
 * languages regardless of system locale. It can deal with expanding values: a
3863
 * German Eszett character can compare against two ASCII 's' chars and be
3864
 * considered a match, for example. A notable exception: it does not handle
3865
 * the Turkish 'i' character; human language is complicated!
3866
 *
3867
 * Since this handles Unicode, it expects the string to be well-formed UTF-8
3868
 * and not a null-terminated string of arbitrary bytes. Bytes that are not
3869
 * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT
3870
 * CHARACTER), which is to say two strings of random bits may turn out to
3871
 * match if they convert to the same amount of replacement characters.
3872
 *
3873
 * \param str1 the first string to compare. NULL is not permitted!
3874
 * \param str2 the second string to compare. NULL is not permitted!
3875
 * \returns less than zero if str1 is "less than" str2, greater than zero if
3876
 *          str1 is "greater than" str2, and zero if the strings match
3877
 *          exactly.
3878
 *
3879
 * \threadsafety It is safe to call this function from any thread.
3880
 *
3881
 * \since This function is available since SDL 3.2.0.
3882
 */
3883
extern SDL_DECLSPEC int SDLCALL SDL_strcasecmp(const char *str1, const char *str2);
3884
3885
3886
/**
3887
 * Compare two UTF-8 strings, case-insensitively, up to a number of bytes.
3888
 *
3889
 * This will work with Unicode strings, using a technique called
3890
 * "case-folding" to handle the vast majority of case-sensitive human
3891
 * languages regardless of system locale. It can deal with expanding values: a
3892
 * German Eszett character can compare against two ASCII 's' chars and be
3893
 * considered a match, for example. A notable exception: it does not handle
3894
 * the Turkish 'i' character; human language is complicated!
3895
 *
3896
 * Since this handles Unicode, it expects the string to be well-formed UTF-8
3897
 * and not a null-terminated string of arbitrary bytes. Bytes that are not
3898
 * valid UTF-8 are treated as Unicode character U+FFFD (REPLACEMENT
3899
 * CHARACTER), which is to say two strings of random bits may turn out to
3900
 * match if they convert to the same amount of replacement characters.
3901
 *
3902
 * Note that while this function is intended to be used with UTF-8, `maxlen`
3903
 * specifies a _byte_ limit! If the limit lands in the middle of a multi-byte
3904
 * UTF-8 sequence, it may convert a portion of the final character to one or
3905
 * more Unicode character U+FFFD (REPLACEMENT CHARACTER) so as not to overflow
3906
 * a buffer.
3907
 *
3908
 * `maxlen` specifies a maximum number of bytes to compare; if the strings
3909
 * match to this number of bytes (or both have matched to a null-terminator
3910
 * character before this number of bytes), they will be considered equal.
3911
 *
3912
 * \param str1 the first string to compare. NULL is not permitted!
3913
 * \param str2 the second string to compare. NULL is not permitted!
3914
 * \param maxlen the maximum number of bytes to compare.
3915
 * \returns less than zero if str1 is "less than" str2, greater than zero if
3916
 *          str1 is "greater than" str2, and zero if the strings match
3917
 *          exactly.
3918
 *
3919
 * \threadsafety It is safe to call this function from any thread.
3920
 *
3921
 * \since This function is available since SDL 3.2.0.
3922
 */
3923
extern SDL_DECLSPEC int SDLCALL SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen);
3924
3925
/**
3926
 * Searches a string for the first occurrence of any character contained in a
3927
 * breakset, and returns a pointer from the string to that character.
3928
 *
3929
 * \param str The null-terminated string to be searched. Must not be NULL, and
3930
 *            must not overlap with `breakset`.
3931
 * \param breakset A null-terminated string containing the list of characters
3932
 *                 to look for. Must not be NULL, and must not overlap with
3933
 *                 `str`.
3934
 * \returns A pointer to the location, in str, of the first occurrence of a
3935
 *          character present in the breakset, or NULL if none is found.
3936
 *
3937
 * \threadsafety It is safe to call this function from any thread.
3938
 *
3939
 * \since This function is available since SDL 3.2.0.
3940
 */
3941
extern SDL_DECLSPEC char * SDLCALL SDL_strpbrk(const char *str, const char *breakset);
3942
3943
/**
3944
 * The Unicode REPLACEMENT CHARACTER codepoint.
3945
 *
3946
 * SDL_StepUTF8() and SDL_StepBackUTF8() report this codepoint when they
3947
 * encounter a UTF-8 string with encoding errors.
3948
 *
3949
 * This tends to render as something like a question mark in most places.
3950
 *
3951
 * \since This macro is available since SDL 3.2.0.
3952
 *
3953
 * \sa SDL_StepBackUTF8
3954
 * \sa SDL_StepUTF8
3955
 */
3956
#define SDL_INVALID_UNICODE_CODEPOINT 0xFFFD
3957
3958
/**
3959
 * Decode a UTF-8 string, one Unicode codepoint at a time.
3960
 *
3961
 * This will return the first Unicode codepoint in the UTF-8 encoded string in
3962
 * `*pstr`, and then advance `*pstr` past any consumed bytes before returning.
3963
 *
3964
 * It will not access more than `*pslen` bytes from the string. `*pslen` will
3965
 * be adjusted, as well, subtracting the number of bytes consumed.
3966
 *
3967
 * `pslen` is allowed to be NULL, in which case the string _must_ be
3968
 * NULL-terminated, as the function will blindly read until it sees the NULL
3969
 * char.
3970
 *
3971
 * if `*pslen` is zero, it assumes the end of string is reached and returns a
3972
 * zero codepoint regardless of the contents of the string buffer.
3973
 *
3974
 * If the resulting codepoint is zero (a NULL terminator), or `*pslen` is
3975
 * zero, it will not advance `*pstr` or `*pslen` at all.
3976
 *
3977
 * Generally this function is called in a loop until it returns zero,
3978
 * adjusting its parameters each iteration.
3979
 *
3980
 * If an invalid UTF-8 sequence is encountered, this function returns
3981
 * SDL_INVALID_UNICODE_CODEPOINT and advances the string/length by one byte
3982
 * (which is to say, a multibyte sequence might produce several
3983
 * SDL_INVALID_UNICODE_CODEPOINT returns before it syncs to the next valid
3984
 * UTF-8 sequence).
3985
 *
3986
 * Several things can generate invalid UTF-8 sequences, including overlong
3987
 * encodings, the use of UTF-16 surrogate values, and truncated data. Please
3988
 * refer to
3989
 * [RFC3629](https://www.ietf.org/rfc/rfc3629.txt)
3990
 * for details.
3991
 *
3992
 * \param pstr a pointer to a UTF-8 string pointer to be read and adjusted.
3993
 * \param pslen a pointer to the number of bytes in the string, to be read and
3994
 *              adjusted. NULL is allowed.
3995
 * \returns the first Unicode codepoint in the string.
3996
 *
3997
 * \threadsafety It is safe to call this function from any thread.
3998
 *
3999
 * \since This function is available since SDL 3.2.0.
4000
 */
4001
extern SDL_DECLSPEC Uint32 SDLCALL SDL_StepUTF8(const char **pstr, size_t *pslen);
4002
4003
/**
4004
 * Decode a UTF-8 string in reverse, one Unicode codepoint at a time.
4005
 *
4006
 * This will go to the start of the previous Unicode codepoint in the string,
4007
 * move `*pstr` to that location and return that codepoint.
4008
 *
4009
 * If `*pstr` is already at the start of the string), it will not advance
4010
 * `*pstr` at all.
4011
 *
4012
 * Generally this function is called in a loop until it returns zero,
4013
 * adjusting its parameter each iteration.
4014
 *
4015
 * If an invalid UTF-8 sequence is encountered, this function returns
4016
 * SDL_INVALID_UNICODE_CODEPOINT.
4017
 *
4018
 * Several things can generate invalid UTF-8 sequences, including overlong
4019
 * encodings, the use of UTF-16 surrogate values, and truncated data. Please
4020
 * refer to
4021
 * [RFC3629](https://www.ietf.org/rfc/rfc3629.txt)
4022
 * for details.
4023
 *
4024
 * \param start a pointer to the beginning of the UTF-8 string.
4025
 * \param pstr a pointer to a UTF-8 string pointer to be read and adjusted.
4026
 * \returns the previous Unicode codepoint in the string.
4027
 *
4028
 * \threadsafety It is safe to call this function from any thread.
4029
 *
4030
 * \since This function is available since SDL 3.2.0.
4031
 */
4032
extern SDL_DECLSPEC Uint32 SDLCALL SDL_StepBackUTF8(const char *start, const char **pstr);
4033
4034
/**
4035
 * Convert a single Unicode codepoint to UTF-8.
4036
 *
4037
 * The buffer pointed to by `dst` must be at least 4 bytes long, as this
4038
 * function may generate between 1 and 4 bytes of output.
4039
 *
4040
 * This function returns the first byte _after_ the newly-written UTF-8
4041
 * sequence, which is useful for encoding multiple codepoints in a loop, or
4042
 * knowing where to write a NULL-terminator character to end the string (in
4043
 * either case, plan to have a buffer of _more_ than 4 bytes!).
4044
 *
4045
 * If `codepoint` is an invalid value (outside the Unicode range, or a UTF-16
4046
 * surrogate value, etc), this will use U+FFFD (REPLACEMENT CHARACTER) for the
4047
 * codepoint instead, and not set an error.
4048
 *
4049
 * If `dst` is NULL, this returns NULL immediately without writing to the
4050
 * pointer and without setting an error.
4051
 *
4052
 * \param codepoint a Unicode codepoint to convert to UTF-8.
4053
 * \param dst the location to write the encoded UTF-8. Must point to at least
4054
 *            4 bytes!
4055
 * \returns the first byte past the newly-written UTF-8 sequence.
4056
 *
4057
 * \threadsafety It is safe to call this function from any thread.
4058
 *
4059
 * \since This function is available since SDL 3.2.0.
4060
 */
4061
extern SDL_DECLSPEC char * SDLCALL SDL_UCS4ToUTF8(Uint32 codepoint, char *dst);
4062
4063
/**
4064
 * This works exactly like sscanf() but doesn't require access to a C runtime.
4065
 *
4066
 * Scan a string, matching a format string, converting each '%' item and
4067
 * storing it to pointers provided through variable arguments.
4068
 *
4069
 * \param text the string to scan. Must not be NULL.
4070
 * \param fmt a printf-style format string. Must not be NULL.
4071
 * \param ... a list of pointers to values to be filled in with scanned items.
4072
 * \returns the number of items that matched the format string.
4073
 *
4074
 * \threadsafety It is safe to call this function from any thread.
4075
 *
4076
 * \since This function is available since SDL 3.2.0.
4077
 */
4078
extern SDL_DECLSPEC int SDLCALL SDL_sscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, ...) SDL_SCANF_VARARG_FUNC(2);
4079
4080
/**
4081
 * This works exactly like vsscanf() but doesn't require access to a C
4082
 * runtime.
4083
 *
4084
 * Functions identically to SDL_sscanf(), except it takes a `va_list` instead
4085
 * of using `...` variable arguments.
4086
 *
4087
 * \param text the string to scan. Must not be NULL.
4088
 * \param fmt a printf-style format string. Must not be NULL.
4089
 * \param ap a `va_list` of pointers to values to be filled in with scanned
4090
 *           items.
4091
 * \returns the number of items that matched the format string.
4092
 *
4093
 * \threadsafety It is safe to call this function from any thread.
4094
 *
4095
 * \since This function is available since SDL 3.2.0.
4096
 */
4097
extern SDL_DECLSPEC int SDLCALL SDL_vsscanf(const char *text, SDL_SCANF_FORMAT_STRING const char *fmt, va_list ap) SDL_SCANF_VARARG_FUNCV(2);
4098
4099
/**
4100
 * This works exactly like snprintf() but doesn't require access to a C
4101
 * runtime.
4102
 *
4103
 * Format a string of up to `maxlen`-1 bytes, converting each '%' item with
4104
 * values provided through variable arguments.
4105
 *
4106
 * While some C runtimes differ on how to deal with too-large strings, this
4107
 * function null-terminates the output, by treating the null-terminator as
4108
 * part of the `maxlen` count. Note that if `maxlen` is zero, however, no
4109
 * bytes will be written at all.
4110
 *
4111
 * This function returns the number of _bytes_ (not _characters_) that should
4112
 * be written, excluding the null-terminator character. If this returns a
4113
 * number >= `maxlen`, it means the output string was truncated. A negative
4114
 * return value means an error occurred.
4115
 *
4116
 * Referencing the output string's pointer with a format item is undefined
4117
 * behavior.
4118
 *
4119
 * \param text the buffer to write the string into. Must not be NULL.
4120
 * \param maxlen the maximum bytes to write, including the null-terminator.
4121
 * \param fmt a printf-style format string. Must not be NULL.
4122
 * \param ... a list of values to be used with the format string.
4123
 * \returns the number of bytes that should be written, not counting the
4124
 *          null-terminator char, or a negative value on error.
4125
 *
4126
 * \threadsafety It is safe to call this function from any thread.
4127
 *
4128
 * \since This function is available since SDL 3.2.0.
4129
 */
4130
extern SDL_DECLSPEC int SDLCALL SDL_snprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(3);
4131
4132
/**
4133
 * This works exactly like swprintf() but doesn't require access to a C
4134
 * runtime.
4135
 *
4136
 * Format a wide string of up to `maxlen`-1 wchar_t values, converting each
4137
 * '%' item with values provided through variable arguments.
4138
 *
4139
 * While some C runtimes differ on how to deal with too-large strings, this
4140
 * function null-terminates the output, by treating the null-terminator as
4141
 * part of the `maxlen` count. Note that if `maxlen` is zero, however, no wide
4142
 * characters will be written at all.
4143
 *
4144
 * This function returns the number of _wide characters_ (not _codepoints_)
4145
 * that should be written, excluding the null-terminator character. If this
4146
 * returns a number >= `maxlen`, it means the output string was truncated. A
4147
 * negative return value means an error occurred.
4148
 *
4149
 * Referencing the output string's pointer with a format item is undefined
4150
 * behavior.
4151
 *
4152
 * \param text the buffer to write the wide string into. Must not be NULL.
4153
 * \param maxlen the maximum wchar_t values to write, including the
4154
 *               null-terminator.
4155
 * \param fmt a printf-style format string. Must not be NULL.
4156
 * \param ... a list of values to be used with the format string.
4157
 * \returns the number of wide characters that should be written, not counting
4158
 *          the null-terminator char, or a negative value on error.
4159
 *
4160
 * \threadsafety It is safe to call this function from any thread.
4161
 *
4162
 * \since This function is available since SDL 3.2.0.
4163
 */
4164
extern SDL_DECLSPEC int SDLCALL SDL_swprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, ...) SDL_WPRINTF_VARARG_FUNC(3);
4165
4166
/**
4167
 * This works exactly like vsnprintf() but doesn't require access to a C
4168
 * runtime.
4169
 *
4170
 * Functions identically to SDL_snprintf(), except it takes a `va_list`
4171
 * instead of using `...` variable arguments.
4172
 *
4173
 * \param text the buffer to write the string into. Must not be NULL.
4174
 * \param maxlen the maximum bytes to write, including the null-terminator.
4175
 * \param fmt a printf-style format string. Must not be NULL.
4176
 * \param ap a `va_list` values to be used with the format string.
4177
 * \returns the number of bytes that should be written, not counting the
4178
 *          null-terminator char, or a negative value on error.
4179
 *
4180
 * \threadsafety It is safe to call this function from any thread.
4181
 *
4182
 * \since This function is available since SDL 3.2.0.
4183
 */
4184
extern SDL_DECLSPEC int SDLCALL SDL_vsnprintf(SDL_OUT_Z_CAP(maxlen) char *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(3);
4185
4186
/**
4187
 * This works exactly like vswprintf() but doesn't require access to a C
4188
 * runtime.
4189
 *
4190
 * Functions identically to SDL_swprintf(), except it takes a `va_list`
4191
 * instead of using `...` variable arguments.
4192
 *
4193
 * \param text the buffer to write the string into. Must not be NULL.
4194
 * \param maxlen the maximum wide characters to write, including the
4195
 *               null-terminator.
4196
 * \param fmt a printf-style format wide string. Must not be NULL.
4197
 * \param ap a `va_list` values to be used with the format string.
4198
 * \returns the number of wide characters that should be written, not counting
4199
 *          the null-terminator char, or a negative value on error.
4200
 *
4201
 * \threadsafety It is safe to call this function from any thread.
4202
 *
4203
 * \since This function is available since SDL 3.2.0.
4204
 */
4205
extern SDL_DECLSPEC int SDLCALL SDL_vswprintf(SDL_OUT_Z_CAP(maxlen) wchar_t *text, size_t maxlen, SDL_PRINTF_FORMAT_STRING const wchar_t *fmt, va_list ap) SDL_WPRINTF_VARARG_FUNCV(3);
4206
4207
/**
4208
 * This works exactly like asprintf() but doesn't require access to a C
4209
 * runtime.
4210
 *
4211
 * Functions identically to SDL_snprintf(), except it allocates a buffer large
4212
 * enough to hold the output string on behalf of the caller.
4213
 *
4214
 * On success, this function returns the number of bytes (not characters)
4215
 * comprising the output string, not counting the null-terminator character,
4216
 * and sets `*strp` to the newly-allocated string.
4217
 *
4218
 * On error, this function returns a negative number, and the value of `*strp`
4219
 * is undefined.
4220
 *
4221
 * The returned string is owned by the caller, and should be passed to
4222
 * SDL_free when no longer needed.
4223
 *
4224
 * \param strp on output, is set to the new string. Must not be NULL.
4225
 * \param fmt a printf-style format string. Must not be NULL.
4226
 * \param ... a list of values to be used with the format string.
4227
 * \returns the number of bytes in the newly-allocated string, not counting
4228
 *          the null-terminator char, or a negative value on error.
4229
 *
4230
 * \threadsafety It is safe to call this function from any thread.
4231
 *
4232
 * \since This function is available since SDL 3.2.0.
4233
 */
4234
extern SDL_DECLSPEC int SDLCALL SDL_asprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, ...) SDL_PRINTF_VARARG_FUNC(2);
4235
4236
/**
4237
 * This works exactly like vasprintf() but doesn't require access to a C
4238
 * runtime.
4239
 *
4240
 * Functions identically to SDL_asprintf(), except it takes a `va_list`
4241
 * instead of using `...` variable arguments.
4242
 *
4243
 * \param strp on output, is set to the new string. Must not be NULL.
4244
 * \param fmt a printf-style format string. Must not be NULL.
4245
 * \param ap a `va_list` values to be used with the format string.
4246
 * \returns the number of bytes in the newly-allocated string, not counting
4247
 *          the null-terminator char, or a negative value on error.
4248
 *
4249
 * \threadsafety It is safe to call this function from any thread.
4250
 *
4251
 * \since This function is available since SDL 3.2.0.
4252
 */
4253
extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) SDL_PRINTF_VARARG_FUNCV(2);
4254
4255
/**
4256
 * Seeds the pseudo-random number generator.
4257
 *
4258
 * Reusing the seed number will cause SDL_rand() to repeat the same stream of
4259
 * 'random' numbers.
4260
 *
4261
 * \param seed the value to use as a random number seed, or 0 to use
4262
 *             SDL_GetPerformanceCounter().
4263
 *
4264
 * \threadsafety This should be called on the same thread that calls
4265
 *               SDL_rand()
4266
 *
4267
 * \since This function is available since SDL 3.2.0.
4268
 *
4269
 * \sa SDL_rand
4270
 * \sa SDL_rand_bits
4271
 * \sa SDL_randf
4272
 */
4273
extern SDL_DECLSPEC void SDLCALL SDL_srand(Uint64 seed);
4274
4275
/**
4276
 * Generate a pseudo-random number less than n for positive n
4277
 *
4278
 * The method used is faster and of better quality than `rand() % n`. Odds are
4279
 * roughly 99.9% even for n = 1 million. Evenness is better for smaller n, and
4280
 * much worse as n gets bigger.
4281
 *
4282
 * Example: to simulate a d6 use `SDL_rand(6) + 1` The +1 converts 0..5 to
4283
 * 1..6
4284
 *
4285
 * If you want to generate a pseudo-random number in the full range of Sint32,
4286
 * you should use: (Sint32)SDL_rand_bits()
4287
 *
4288
 * If you want reproducible output, be sure to initialize with SDL_srand()
4289
 * first.
4290
 *
4291
 * There are no guarantees as to the quality of the random sequence produced,
4292
 * and this should not be used for security (cryptography, passwords) or where
4293
 * money is on the line (loot-boxes, casinos). There are many random number
4294
 * libraries available with different characteristics and you should pick one
4295
 * of those to meet any serious needs.
4296
 *
4297
 * \param n the number of possible outcomes. n must be positive.
4298
 * \returns a random value in the range of [0 .. n-1].
4299
 *
4300
 * \threadsafety All calls should be made from a single thread
4301
 *
4302
 * \since This function is available since SDL 3.2.0.
4303
 *
4304
 * \sa SDL_srand
4305
 * \sa SDL_randf
4306
 */
4307
extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand(Sint32 n);
4308
4309
/**
4310
 * Generate a uniform pseudo-random floating point number less than 1.0
4311
 *
4312
 * If you want reproducible output, be sure to initialize with SDL_srand()
4313
 * first.
4314
 *
4315
 * There are no guarantees as to the quality of the random sequence produced,
4316
 * and this should not be used for security (cryptography, passwords) or where
4317
 * money is on the line (loot-boxes, casinos). There are many random number
4318
 * libraries available with different characteristics and you should pick one
4319
 * of those to meet any serious needs.
4320
 *
4321
 * \returns a random value in the range of [0.0, 1.0).
4322
 *
4323
 * \threadsafety All calls should be made from a single thread
4324
 *
4325
 * \since This function is available since SDL 3.2.0.
4326
 *
4327
 * \sa SDL_srand
4328
 * \sa SDL_rand
4329
 */
4330
extern SDL_DECLSPEC float SDLCALL SDL_randf(void);
4331
4332
/**
4333
 * Generate 32 pseudo-random bits.
4334
 *
4335
 * You likely want to use SDL_rand() to get a psuedo-random number instead.
4336
 *
4337
 * There are no guarantees as to the quality of the random sequence produced,
4338
 * and this should not be used for security (cryptography, passwords) or where
4339
 * money is on the line (loot-boxes, casinos). There are many random number
4340
 * libraries available with different characteristics and you should pick one
4341
 * of those to meet any serious needs.
4342
 *
4343
 * \returns a random value in the range of [0-SDL_MAX_UINT32].
4344
 *
4345
 * \threadsafety All calls should be made from a single thread
4346
 *
4347
 * \since This function is available since SDL 3.2.0.
4348
 *
4349
 * \sa SDL_rand
4350
 * \sa SDL_randf
4351
 * \sa SDL_srand
4352
 */
4353
extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits(void);
4354
4355
/**
4356
 * Generate a pseudo-random number less than n for positive n
4357
 *
4358
 * The method used is faster and of better quality than `rand() % n`. Odds are
4359
 * roughly 99.9% even for n = 1 million. Evenness is better for smaller n, and
4360
 * much worse as n gets bigger.
4361
 *
4362
 * Example: to simulate a d6 use `SDL_rand_r(state, 6) + 1` The +1 converts
4363
 * 0..5 to 1..6
4364
 *
4365
 * If you want to generate a pseudo-random number in the full range of Sint32,
4366
 * you should use: (Sint32)SDL_rand_bits_r(state)
4367
 *
4368
 * There are no guarantees as to the quality of the random sequence produced,
4369
 * and this should not be used for security (cryptography, passwords) or where
4370
 * money is on the line (loot-boxes, casinos). There are many random number
4371
 * libraries available with different characteristics and you should pick one
4372
 * of those to meet any serious needs.
4373
 *
4374
 * \param state a pointer to the current random number state, this may not be
4375
 *              NULL.
4376
 * \param n the number of possible outcomes. n must be positive.
4377
 * \returns a random value in the range of [0 .. n-1].
4378
 *
4379
 * \threadsafety This function is thread-safe, as long as the state pointer
4380
 *               isn't shared between threads.
4381
 *
4382
 * \since This function is available since SDL 3.2.0.
4383
 *
4384
 * \sa SDL_rand
4385
 * \sa SDL_rand_bits_r
4386
 * \sa SDL_randf_r
4387
 */
4388
extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_r(Uint64 *state, Sint32 n);
4389
4390
/**
4391
 * Generate a uniform pseudo-random floating point number less than 1.0
4392
 *
4393
 * If you want reproducible output, be sure to initialize with SDL_srand()
4394
 * first.
4395
 *
4396
 * There are no guarantees as to the quality of the random sequence produced,
4397
 * and this should not be used for security (cryptography, passwords) or where
4398
 * money is on the line (loot-boxes, casinos). There are many random number
4399
 * libraries available with different characteristics and you should pick one
4400
 * of those to meet any serious needs.
4401
 *
4402
 * \param state a pointer to the current random number state, this may not be
4403
 *              NULL.
4404
 * \returns a random value in the range of [0.0, 1.0).
4405
 *
4406
 * \threadsafety This function is thread-safe, as long as the state pointer
4407
 *               isn't shared between threads.
4408
 *
4409
 * \since This function is available since SDL 3.2.0.
4410
 *
4411
 * \sa SDL_rand_bits_r
4412
 * \sa SDL_rand_r
4413
 * \sa SDL_randf
4414
 */
4415
extern SDL_DECLSPEC float SDLCALL SDL_randf_r(Uint64 *state);
4416
4417
/**
4418
 * Generate 32 pseudo-random bits.
4419
 *
4420
 * You likely want to use SDL_rand_r() to get a psuedo-random number instead.
4421
 *
4422
 * There are no guarantees as to the quality of the random sequence produced,
4423
 * and this should not be used for security (cryptography, passwords) or where
4424
 * money is on the line (loot-boxes, casinos). There are many random number
4425
 * libraries available with different characteristics and you should pick one
4426
 * of those to meet any serious needs.
4427
 *
4428
 * \param state a pointer to the current random number state, this may not be
4429
 *              NULL.
4430
 * \returns a random value in the range of [0-SDL_MAX_UINT32].
4431
 *
4432
 * \threadsafety This function is thread-safe, as long as the state pointer
4433
 *               isn't shared between threads.
4434
 *
4435
 * \since This function is available since SDL 3.2.0.
4436
 *
4437
 * \sa SDL_rand_r
4438
 * \sa SDL_randf_r
4439
 */
4440
extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits_r(Uint64 *state);
4441
4442
#ifndef SDL_PI_D
4443
4444
/**
4445
 * The value of Pi, as a double-precision floating point literal.
4446
 *
4447
 * \since This macro is available since SDL 3.2.0.
4448
 *
4449
 * \sa SDL_PI_F
4450
 */
4451
#define SDL_PI_D   3.141592653589793238462643383279502884       /**< pi (double) */
4452
#endif
4453
4454
#ifndef SDL_PI_F
4455
4456
/**
4457
 * The value of Pi, as a single-precision floating point literal.
4458
 *
4459
 * \since This macro is available since SDL 3.2.0.
4460
 *
4461
 * \sa SDL_PI_D
4462
 */
4463
#define SDL_PI_F   3.141592653589793238462643383279502884F      /**< pi (float) */
4464
#endif
4465
4466
/**
4467
 * Compute the arc cosine of `x`.
4468
 *
4469
 * The definition of `y = acos(x)` is `x = cos(y)`.
4470
 *
4471
 * Domain: `-1 <= x <= 1`
4472
 *
4473
 * Range: `0 <= y <= Pi`
4474
 *
4475
 * This function operates on double-precision floating point values, use
4476
 * SDL_acosf for single-precision floats.
4477
 *
4478
 * This function may use a different approximation across different versions,
4479
 * platforms and configurations. i.e, it can return a different value given
4480
 * the same input on different machines or operating systems, or if SDL is
4481
 * updated.
4482
 *
4483
 * \param x floating point value.
4484
 * \returns arc cosine of `x`, in radians.
4485
 *
4486
 * \threadsafety It is safe to call this function from any thread.
4487
 *
4488
 * \since This function is available since SDL 3.2.0.
4489
 *
4490
 * \sa SDL_acosf
4491
 * \sa SDL_asin
4492
 * \sa SDL_cos
4493
 */
4494
extern SDL_DECLSPEC double SDLCALL SDL_acos(double x);
4495
4496
/**
4497
 * Compute the arc cosine of `x`.
4498
 *
4499
 * The definition of `y = acos(x)` is `x = cos(y)`.
4500
 *
4501
 * Domain: `-1 <= x <= 1`
4502
 *
4503
 * Range: `0 <= y <= Pi`
4504
 *
4505
 * This function operates on single-precision floating point values, use
4506
 * SDL_acos for double-precision floats.
4507
 *
4508
 * This function may use a different approximation across different versions,
4509
 * platforms and configurations. i.e, it can return a different value given
4510
 * the same input on different machines or operating systems, or if SDL is
4511
 * updated.
4512
 *
4513
 * \param x floating point value.
4514
 * \returns arc cosine of `x`, in radians.
4515
 *
4516
 * \threadsafety It is safe to call this function from any thread.
4517
 *
4518
 * \since This function is available since SDL 3.2.0.
4519
 *
4520
 * \sa SDL_acos
4521
 * \sa SDL_asinf
4522
 * \sa SDL_cosf
4523
 */
4524
extern SDL_DECLSPEC float SDLCALL SDL_acosf(float x);
4525
4526
/**
4527
 * Compute the arc sine of `x`.
4528
 *
4529
 * The definition of `y = asin(x)` is `x = sin(y)`.
4530
 *
4531
 * Domain: `-1 <= x <= 1`
4532
 *
4533
 * Range: `-Pi/2 <= y <= Pi/2`
4534
 *
4535
 * This function operates on double-precision floating point values, use
4536
 * SDL_asinf for single-precision floats.
4537
 *
4538
 * This function may use a different approximation across different versions,
4539
 * platforms and configurations. i.e, it can return a different value given
4540
 * the same input on different machines or operating systems, or if SDL is
4541
 * updated.
4542
 *
4543
 * \param x floating point value.
4544
 * \returns arc sine of `x`, in radians.
4545
 *
4546
 * \threadsafety It is safe to call this function from any thread.
4547
 *
4548
 * \since This function is available since SDL 3.2.0.
4549
 *
4550
 * \sa SDL_asinf
4551
 * \sa SDL_acos
4552
 * \sa SDL_sin
4553
 */
4554
extern SDL_DECLSPEC double SDLCALL SDL_asin(double x);
4555
4556
/**
4557
 * Compute the arc sine of `x`.
4558
 *
4559
 * The definition of `y = asin(x)` is `x = sin(y)`.
4560
 *
4561
 * Domain: `-1 <= x <= 1`
4562
 *
4563
 * Range: `-Pi/2 <= y <= Pi/2`
4564
 *
4565
 * This function operates on single-precision floating point values, use
4566
 * SDL_asin for double-precision floats.
4567
 *
4568
 * This function may use a different approximation across different versions,
4569
 * platforms and configurations. i.e, it can return a different value given
4570
 * the same input on different machines or operating systems, or if SDL is
4571
 * updated.
4572
 *
4573
 * \param x floating point value.
4574
 * \returns arc sine of `x`, in radians.
4575
 *
4576
 * \threadsafety It is safe to call this function from any thread.
4577
 *
4578
 * \since This function is available since SDL 3.2.0.
4579
 *
4580
 * \sa SDL_asin
4581
 * \sa SDL_acosf
4582
 * \sa SDL_sinf
4583
 */
4584
extern SDL_DECLSPEC float SDLCALL SDL_asinf(float x);
4585
4586
/**
4587
 * Compute the arc tangent of `x`.
4588
 *
4589
 * The definition of `y = atan(x)` is `x = tan(y)`.
4590
 *
4591
 * Domain: `-INF <= x <= INF`
4592
 *
4593
 * Range: `-Pi/2 <= y <= Pi/2`
4594
 *
4595
 * This function operates on double-precision floating point values, use
4596
 * SDL_atanf for single-precision floats.
4597
 *
4598
 * To calculate the arc tangent of y / x, use SDL_atan2.
4599
 *
4600
 * This function may use a different approximation across different versions,
4601
 * platforms and configurations. i.e, it can return a different value given
4602
 * the same input on different machines or operating systems, or if SDL is
4603
 * updated.
4604
 *
4605
 * \param x floating point value.
4606
 * \returns arc tangent of of `x` in radians, or 0 if `x = 0`.
4607
 *
4608
 * \threadsafety It is safe to call this function from any thread.
4609
 *
4610
 * \since This function is available since SDL 3.2.0.
4611
 *
4612
 * \sa SDL_atanf
4613
 * \sa SDL_atan2
4614
 * \sa SDL_tan
4615
 */
4616
extern SDL_DECLSPEC double SDLCALL SDL_atan(double x);
4617
4618
/**
4619
 * Compute the arc tangent of `x`.
4620
 *
4621
 * The definition of `y = atan(x)` is `x = tan(y)`.
4622
 *
4623
 * Domain: `-INF <= x <= INF`
4624
 *
4625
 * Range: `-Pi/2 <= y <= Pi/2`
4626
 *
4627
 * This function operates on single-precision floating point values, use
4628
 * SDL_atan for dboule-precision floats.
4629
 *
4630
 * To calculate the arc tangent of y / x, use SDL_atan2f.
4631
 *
4632
 * This function may use a different approximation across different versions,
4633
 * platforms and configurations. i.e, it can return a different value given
4634
 * the same input on different machines or operating systems, or if SDL is
4635
 * updated.
4636
 *
4637
 * \param x floating point value.
4638
 * \returns arc tangent of of `x` in radians, or 0 if `x = 0`.
4639
 *
4640
 * \threadsafety It is safe to call this function from any thread.
4641
 *
4642
 * \since This function is available since SDL 3.2.0.
4643
 *
4644
 * \sa SDL_atan
4645
 * \sa SDL_atan2f
4646
 * \sa SDL_tanf
4647
 */
4648
extern SDL_DECLSPEC float SDLCALL SDL_atanf(float x);
4649
4650
/**
4651
 * Compute the arc tangent of `y / x`, using the signs of x and y to adjust
4652
 * the result's quadrant.
4653
 *
4654
 * The definition of `z = atan2(x, y)` is `y = x tan(z)`, where the quadrant
4655
 * of z is determined based on the signs of x and y.
4656
 *
4657
 * Domain: `-INF <= x <= INF`, `-INF <= y <= INF`
4658
 *
4659
 * Range: `-Pi <= y <= Pi`
4660
 *
4661
 * This function operates on double-precision floating point values, use
4662
 * SDL_atan2f for single-precision floats.
4663
 *
4664
 * To calculate the arc tangent of a single value, use SDL_atan.
4665
 *
4666
 * This function may use a different approximation across different versions,
4667
 * platforms and configurations. i.e, it can return a different value given
4668
 * the same input on different machines or operating systems, or if SDL is
4669
 * updated.
4670
 *
4671
 * \param y floating point value of the numerator (y coordinate).
4672
 * \param x floating point value of the denominator (x coordinate).
4673
 * \returns arc tangent of of `y / x` in radians, or, if `x = 0`, either
4674
 *          `-Pi/2`, `0`, or `Pi/2`, depending on the value of `y`.
4675
 *
4676
 * \threadsafety It is safe to call this function from any thread.
4677
 *
4678
 * \since This function is available since SDL 3.2.0.
4679
 *
4680
 * \sa SDL_atan2f
4681
 * \sa SDL_atan
4682
 * \sa SDL_tan
4683
 */
4684
extern SDL_DECLSPEC double SDLCALL SDL_atan2(double y, double x);
4685
4686
/**
4687
 * Compute the arc tangent of `y / x`, using the signs of x and y to adjust
4688
 * the result's quadrant.
4689
 *
4690
 * The definition of `z = atan2(x, y)` is `y = x tan(z)`, where the quadrant
4691
 * of z is determined based on the signs of x and y.
4692
 *
4693
 * Domain: `-INF <= x <= INF`, `-INF <= y <= INF`
4694
 *
4695
 * Range: `-Pi <= y <= Pi`
4696
 *
4697
 * This function operates on single-precision floating point values, use
4698
 * SDL_atan2 for double-precision floats.
4699
 *
4700
 * To calculate the arc tangent of a single value, use SDL_atanf.
4701
 *
4702
 * This function may use a different approximation across different versions,
4703
 * platforms and configurations. i.e, it can return a different value given
4704
 * the same input on different machines or operating systems, or if SDL is
4705
 * updated.
4706
 *
4707
 * \param y floating point value of the numerator (y coordinate).
4708
 * \param x floating point value of the denominator (x coordinate).
4709
 * \returns arc tangent of of `y / x` in radians, or, if `x = 0`, either
4710
 *          `-Pi/2`, `0`, or `Pi/2`, depending on the value of `y`.
4711
 *
4712
 * \threadsafety It is safe to call this function from any thread.
4713
 *
4714
 * \since This function is available since SDL 3.2.0.
4715
 *
4716
 * \sa SDL_atan2
4717
 * \sa SDL_atan
4718
 * \sa SDL_tan
4719
 */
4720
extern SDL_DECLSPEC float SDLCALL SDL_atan2f(float y, float x);
4721
4722
/**
4723
 * Compute the ceiling of `x`.
4724
 *
4725
 * The ceiling of `x` is the smallest integer `y` such that `y > x`, i.e `x`
4726
 * rounded up to the nearest integer.
4727
 *
4728
 * Domain: `-INF <= x <= INF`
4729
 *
4730
 * Range: `-INF <= y <= INF`, y integer
4731
 *
4732
 * This function operates on double-precision floating point values, use
4733
 * SDL_ceilf for single-precision floats.
4734
 *
4735
 * \param x floating point value.
4736
 * \returns the ceiling of `x`.
4737
 *
4738
 * \threadsafety It is safe to call this function from any thread.
4739
 *
4740
 * \since This function is available since SDL 3.2.0.
4741
 *
4742
 * \sa SDL_ceilf
4743
 * \sa SDL_floor
4744
 * \sa SDL_trunc
4745
 * \sa SDL_round
4746
 * \sa SDL_lround
4747
 */
4748
extern SDL_DECLSPEC double SDLCALL SDL_ceil(double x);
4749
4750
/**
4751
 * Compute the ceiling of `x`.
4752
 *
4753
 * The ceiling of `x` is the smallest integer `y` such that `y > x`, i.e `x`
4754
 * rounded up to the nearest integer.
4755
 *
4756
 * Domain: `-INF <= x <= INF`
4757
 *
4758
 * Range: `-INF <= y <= INF`, y integer
4759
 *
4760
 * This function operates on single-precision floating point values, use
4761
 * SDL_ceil for double-precision floats.
4762
 *
4763
 * \param x floating point value.
4764
 * \returns the ceiling of `x`.
4765
 *
4766
 * \threadsafety It is safe to call this function from any thread.
4767
 *
4768
 * \since This function is available since SDL 3.2.0.
4769
 *
4770
 * \sa SDL_ceil
4771
 * \sa SDL_floorf
4772
 * \sa SDL_truncf
4773
 * \sa SDL_roundf
4774
 * \sa SDL_lroundf
4775
 */
4776
extern SDL_DECLSPEC float SDLCALL SDL_ceilf(float x);
4777
4778
/**
4779
 * Copy the sign of one floating-point value to another.
4780
 *
4781
 * The definition of copysign is that ``copysign(x, y) = abs(x) * sign(y)``.
4782
 *
4783
 * Domain: `-INF <= x <= INF`, ``-INF <= y <= f``
4784
 *
4785
 * Range: `-INF <= z <= INF`
4786
 *
4787
 * This function operates on double-precision floating point values, use
4788
 * SDL_copysignf for single-precision floats.
4789
 *
4790
 * \param x floating point value to use as the magnitude.
4791
 * \param y floating point value to use as the sign.
4792
 * \returns the floating point value with the sign of y and the magnitude of
4793
 *          x.
4794
 *
4795
 * \threadsafety It is safe to call this function from any thread.
4796
 *
4797
 * \since This function is available since SDL 3.2.0.
4798
 *
4799
 * \sa SDL_copysignf
4800
 * \sa SDL_fabs
4801
 */
4802
extern SDL_DECLSPEC double SDLCALL SDL_copysign(double x, double y);
4803
4804
/**
4805
 * Copy the sign of one floating-point value to another.
4806
 *
4807
 * The definition of copysign is that ``copysign(x, y) = abs(x) * sign(y)``.
4808
 *
4809
 * Domain: `-INF <= x <= INF`, ``-INF <= y <= f``
4810
 *
4811
 * Range: `-INF <= z <= INF`
4812
 *
4813
 * This function operates on single-precision floating point values, use
4814
 * SDL_copysign for double-precision floats.
4815
 *
4816
 * \param x floating point value to use as the magnitude.
4817
 * \param y floating point value to use as the sign.
4818
 * \returns the floating point value with the sign of y and the magnitude of
4819
 *          x.
4820
 *
4821
 * \threadsafety It is safe to call this function from any thread.
4822
 *
4823
 * \since This function is available since SDL 3.2.0.
4824
 *
4825
 * \sa SDL_copysign
4826
 * \sa SDL_fabsf
4827
 */
4828
extern SDL_DECLSPEC float SDLCALL SDL_copysignf(float x, float y);
4829
4830
/**
4831
 * Compute the cosine of `x`.
4832
 *
4833
 * Domain: `-INF <= x <= INF`
4834
 *
4835
 * Range: `-1 <= y <= 1`
4836
 *
4837
 * This function operates on double-precision floating point values, use
4838
 * SDL_cosf for single-precision floats.
4839
 *
4840
 * This function may use a different approximation across different versions,
4841
 * platforms and configurations. i.e, it can return a different value given
4842
 * the same input on different machines or operating systems, or if SDL is
4843
 * updated.
4844
 *
4845
 * \param x floating point value, in radians.
4846
 * \returns cosine of `x`.
4847
 *
4848
 * \threadsafety It is safe to call this function from any thread.
4849
 *
4850
 * \since This function is available since SDL 3.2.0.
4851
 *
4852
 * \sa SDL_cosf
4853
 * \sa SDL_acos
4854
 * \sa SDL_sin
4855
 */
4856
extern SDL_DECLSPEC double SDLCALL SDL_cos(double x);
4857
4858
/**
4859
 * Compute the cosine of `x`.
4860
 *
4861
 * Domain: `-INF <= x <= INF`
4862
 *
4863
 * Range: `-1 <= y <= 1`
4864
 *
4865
 * This function operates on single-precision floating point values, use
4866
 * SDL_cos for double-precision floats.
4867
 *
4868
 * This function may use a different approximation across different versions,
4869
 * platforms and configurations. i.e, it can return a different value given
4870
 * the same input on different machines or operating systems, or if SDL is
4871
 * updated.
4872
 *
4873
 * \param x floating point value, in radians.
4874
 * \returns cosine of `x`.
4875
 *
4876
 * \threadsafety It is safe to call this function from any thread.
4877
 *
4878
 * \since This function is available since SDL 3.2.0.
4879
 *
4880
 * \sa SDL_cos
4881
 * \sa SDL_acosf
4882
 * \sa SDL_sinf
4883
 */
4884
extern SDL_DECLSPEC float SDLCALL SDL_cosf(float x);
4885
4886
/**
4887
 * Compute the exponential of `x`.
4888
 *
4889
 * The definition of `y = exp(x)` is `y = e^x`, where `e` is the base of the
4890
 * natural logarithm. The inverse is the natural logarithm, SDL_log.
4891
 *
4892
 * Domain: `-INF <= x <= INF`
4893
 *
4894
 * Range: `0 <= y <= INF`
4895
 *
4896
 * The output will overflow if `exp(x)` is too large to be represented.
4897
 *
4898
 * This function operates on double-precision floating point values, use
4899
 * SDL_expf for single-precision floats.
4900
 *
4901
 * This function may use a different approximation across different versions,
4902
 * platforms and configurations. i.e, it can return a different value given
4903
 * the same input on different machines or operating systems, or if SDL is
4904
 * updated.
4905
 *
4906
 * \param x floating point value.
4907
 * \returns value of `e^x`.
4908
 *
4909
 * \threadsafety It is safe to call this function from any thread.
4910
 *
4911
 * \since This function is available since SDL 3.2.0.
4912
 *
4913
 * \sa SDL_expf
4914
 * \sa SDL_log
4915
 */
4916
extern SDL_DECLSPEC double SDLCALL SDL_exp(double x);
4917
4918
/**
4919
 * Compute the exponential of `x`.
4920
 *
4921
 * The definition of `y = exp(x)` is `y = e^x`, where `e` is the base of the
4922
 * natural logarithm. The inverse is the natural logarithm, SDL_logf.
4923
 *
4924
 * Domain: `-INF <= x <= INF`
4925
 *
4926
 * Range: `0 <= y <= INF`
4927
 *
4928
 * The output will overflow if `exp(x)` is too large to be represented.
4929
 *
4930
 * This function operates on single-precision floating point values, use
4931
 * SDL_exp for double-precision floats.
4932
 *
4933
 * This function may use a different approximation across different versions,
4934
 * platforms and configurations. i.e, it can return a different value given
4935
 * the same input on different machines or operating systems, or if SDL is
4936
 * updated.
4937
 *
4938
 * \param x floating point value.
4939
 * \returns value of `e^x`.
4940
 *
4941
 * \threadsafety It is safe to call this function from any thread.
4942
 *
4943
 * \since This function is available since SDL 3.2.0.
4944
 *
4945
 * \sa SDL_exp
4946
 * \sa SDL_logf
4947
 */
4948
extern SDL_DECLSPEC float SDLCALL SDL_expf(float x);
4949
4950
/**
4951
 * Compute the absolute value of `x`
4952
 *
4953
 * Domain: `-INF <= x <= INF`
4954
 *
4955
 * Range: `0 <= y <= INF`
4956
 *
4957
 * This function operates on double-precision floating point values, use
4958
 * SDL_fabsf for single-precision floats.
4959
 *
4960
 * \param x floating point value to use as the magnitude.
4961
 * \returns the absolute value of `x`.
4962
 *
4963
 * \threadsafety It is safe to call this function from any thread.
4964
 *
4965
 * \since This function is available since SDL 3.2.0.
4966
 *
4967
 * \sa SDL_fabsf
4968
 */
4969
extern SDL_DECLSPEC double SDLCALL SDL_fabs(double x);
4970
4971
/**
4972
 * Compute the absolute value of `x`
4973
 *
4974
 * Domain: `-INF <= x <= INF`
4975
 *
4976
 * Range: `0 <= y <= INF`
4977
 *
4978
 * This function operates on single-precision floating point values, use
4979
 * SDL_fabs for double-precision floats.
4980
 *
4981
 * \param x floating point value to use as the magnitude.
4982
 * \returns the absolute value of `x`.
4983
 *
4984
 * \threadsafety It is safe to call this function from any thread.
4985
 *
4986
 * \since This function is available since SDL 3.2.0.
4987
 *
4988
 * \sa SDL_fabs
4989
 */
4990
extern SDL_DECLSPEC float SDLCALL SDL_fabsf(float x);
4991
4992
/**
4993
 * Compute the floor of `x`.
4994
 *
4995
 * The floor of `x` is the largest integer `y` such that `y > x`, i.e `x`
4996
 * rounded down to the nearest integer.
4997
 *
4998
 * Domain: `-INF <= x <= INF`
4999
 *
5000
 * Range: `-INF <= y <= INF`, y integer
5001
 *
5002
 * This function operates on double-precision floating point values, use
5003
 * SDL_floorf for single-precision floats.
5004
 *
5005
 * \param x floating point value.
5006
 * \returns the floor of `x`.
5007
 *
5008
 * \threadsafety It is safe to call this function from any thread.
5009
 *
5010
 * \since This function is available since SDL 3.2.0.
5011
 *
5012
 * \sa SDL_floorf
5013
 * \sa SDL_ceil
5014
 * \sa SDL_trunc
5015
 * \sa SDL_round
5016
 * \sa SDL_lround
5017
 */
5018
extern SDL_DECLSPEC double SDLCALL SDL_floor(double x);
5019
5020
/**
5021
 * Compute the floor of `x`.
5022
 *
5023
 * The floor of `x` is the largest integer `y` such that `y > x`, i.e `x`
5024
 * rounded down to the nearest integer.
5025
 *
5026
 * Domain: `-INF <= x <= INF`
5027
 *
5028
 * Range: `-INF <= y <= INF`, y integer
5029
 *
5030
 * This function operates on single-precision floating point values, use
5031
 * SDL_floor for double-precision floats.
5032
 *
5033
 * \param x floating point value.
5034
 * \returns the floor of `x`.
5035
 *
5036
 * \threadsafety It is safe to call this function from any thread.
5037
 *
5038
 * \since This function is available since SDL 3.2.0.
5039
 *
5040
 * \sa SDL_floor
5041
 * \sa SDL_ceilf
5042
 * \sa SDL_truncf
5043
 * \sa SDL_roundf
5044
 * \sa SDL_lroundf
5045
 */
5046
extern SDL_DECLSPEC float SDLCALL SDL_floorf(float x);
5047
5048
/**
5049
 * Truncate `x` to an integer.
5050
 *
5051
 * Rounds `x` to the next closest integer to 0. This is equivalent to removing
5052
 * the fractional part of `x`, leaving only the integer part.
5053
 *
5054
 * Domain: `-INF <= x <= INF`
5055
 *
5056
 * Range: `-INF <= y <= INF`, y integer
5057
 *
5058
 * This function operates on double-precision floating point values, use
5059
 * SDL_truncf for single-precision floats.
5060
 *
5061
 * \param x floating point value.
5062
 * \returns `x` truncated to an integer.
5063
 *
5064
 * \threadsafety It is safe to call this function from any thread.
5065
 *
5066
 * \since This function is available since SDL 3.2.0.
5067
 *
5068
 * \sa SDL_truncf
5069
 * \sa SDL_fmod
5070
 * \sa SDL_ceil
5071
 * \sa SDL_floor
5072
 * \sa SDL_round
5073
 * \sa SDL_lround
5074
 */
5075
extern SDL_DECLSPEC double SDLCALL SDL_trunc(double x);
5076
5077
/**
5078
 * Truncate `x` to an integer.
5079
 *
5080
 * Rounds `x` to the next closest integer to 0. This is equivalent to removing
5081
 * the fractional part of `x`, leaving only the integer part.
5082
 *
5083
 * Domain: `-INF <= x <= INF`
5084
 *
5085
 * Range: `-INF <= y <= INF`, y integer
5086
 *
5087
 * This function operates on single-precision floating point values, use
5088
 * SDL_trunc for double-precision floats.
5089
 *
5090
 * \param x floating point value.
5091
 * \returns `x` truncated to an integer.
5092
 *
5093
 * \threadsafety It is safe to call this function from any thread.
5094
 *
5095
 * \since This function is available since SDL 3.2.0.
5096
 *
5097
 * \sa SDL_trunc
5098
 * \sa SDL_fmodf
5099
 * \sa SDL_ceilf
5100
 * \sa SDL_floorf
5101
 * \sa SDL_roundf
5102
 * \sa SDL_lroundf
5103
 */
5104
extern SDL_DECLSPEC float SDLCALL SDL_truncf(float x);
5105
5106
/**
5107
 * Return the floating-point remainder of `x / y`
5108
 *
5109
 * Divides `x` by `y`, and returns the remainder.
5110
 *
5111
 * Domain: `-INF <= x <= INF`, `-INF <= y <= INF`, `y != 0`
5112
 *
5113
 * Range: `-y <= z <= y`
5114
 *
5115
 * This function operates on double-precision floating point values, use
5116
 * SDL_fmodf for single-precision floats.
5117
 *
5118
 * \param x the numerator.
5119
 * \param y the denominator. Must not be 0.
5120
 * \returns the remainder of `x / y`.
5121
 *
5122
 * \threadsafety It is safe to call this function from any thread.
5123
 *
5124
 * \since This function is available since SDL 3.2.0.
5125
 *
5126
 * \sa SDL_fmodf
5127
 * \sa SDL_modf
5128
 * \sa SDL_trunc
5129
 * \sa SDL_ceil
5130
 * \sa SDL_floor
5131
 * \sa SDL_round
5132
 * \sa SDL_lround
5133
 */
5134
extern SDL_DECLSPEC double SDLCALL SDL_fmod(double x, double y);
5135
5136
/**
5137
 * Return the floating-point remainder of `x / y`
5138
 *
5139
 * Divides `x` by `y`, and returns the remainder.
5140
 *
5141
 * Domain: `-INF <= x <= INF`, `-INF <= y <= INF`, `y != 0`
5142
 *
5143
 * Range: `-y <= z <= y`
5144
 *
5145
 * This function operates on single-precision floating point values, use
5146
 * SDL_fmod for double-precision floats.
5147
 *
5148
 * \param x the numerator.
5149
 * \param y the denominator. Must not be 0.
5150
 * \returns the remainder of `x / y`.
5151
 *
5152
 * \threadsafety It is safe to call this function from any thread.
5153
 *
5154
 * \since This function is available since SDL 3.2.0.
5155
 *
5156
 * \sa SDL_fmod
5157
 * \sa SDL_truncf
5158
 * \sa SDL_modff
5159
 * \sa SDL_ceilf
5160
 * \sa SDL_floorf
5161
 * \sa SDL_roundf
5162
 * \sa SDL_lroundf
5163
 */
5164
extern SDL_DECLSPEC float SDLCALL SDL_fmodf(float x, float y);
5165
5166
/**
5167
 * Return whether the value is infinity.
5168
 *
5169
 * \param x double-precision floating point value.
5170
 * \returns non-zero if the value is infinity, 0 otherwise.
5171
 *
5172
 * \threadsafety It is safe to call this function from any thread.
5173
 *
5174
 * \since This function is available since SDL 3.2.0.
5175
 *
5176
 * \sa SDL_isinff
5177
 */
5178
extern SDL_DECLSPEC int SDLCALL SDL_isinf(double x);
5179
5180
/**
5181
 * Return whether the value is infinity.
5182
 *
5183
 * \param x floating point value.
5184
 * \returns non-zero if the value is infinity, 0 otherwise.
5185
 *
5186
 * \threadsafety It is safe to call this function from any thread.
5187
 *
5188
 * \since This function is available since SDL 3.2.0.
5189
 *
5190
 * \sa SDL_isinf
5191
 */
5192
extern SDL_DECLSPEC int SDLCALL SDL_isinff(float x);
5193
5194
/**
5195
 * Return whether the value is NaN.
5196
 *
5197
 * \param x double-precision floating point value.
5198
 * \returns non-zero if the value is NaN, 0 otherwise.
5199
 *
5200
 * \threadsafety It is safe to call this function from any thread.
5201
 *
5202
 * \since This function is available since SDL 3.2.0.
5203
 *
5204
 * \sa SDL_isnanf
5205
 */
5206
extern SDL_DECLSPEC int SDLCALL SDL_isnan(double x);
5207
5208
/**
5209
 * Return whether the value is NaN.
5210
 *
5211
 * \param x floating point value.
5212
 * \returns non-zero if the value is NaN, 0 otherwise.
5213
 *
5214
 * \threadsafety It is safe to call this function from any thread.
5215
 *
5216
 * \since This function is available since SDL 3.2.0.
5217
 *
5218
 * \sa SDL_isnan
5219
 */
5220
extern SDL_DECLSPEC int SDLCALL SDL_isnanf(float x);
5221
5222
/**
5223
 * Compute the natural logarithm of `x`.
5224
 *
5225
 * Domain: `0 < x <= INF`
5226
 *
5227
 * Range: `-INF <= y <= INF`
5228
 *
5229
 * It is an error for `x` to be less than or equal to 0.
5230
 *
5231
 * This function operates on double-precision floating point values, use
5232
 * SDL_logf for single-precision floats.
5233
 *
5234
 * This function may use a different approximation across different versions,
5235
 * platforms and configurations. i.e, it can return a different value given
5236
 * the same input on different machines or operating systems, or if SDL is
5237
 * updated.
5238
 *
5239
 * \param x floating point value. Must be greater than 0.
5240
 * \returns the natural logarithm of `x`.
5241
 *
5242
 * \threadsafety It is safe to call this function from any thread.
5243
 *
5244
 * \since This function is available since SDL 3.2.0.
5245
 *
5246
 * \sa SDL_logf
5247
 * \sa SDL_log10
5248
 * \sa SDL_exp
5249
 */
5250
extern SDL_DECLSPEC double SDLCALL SDL_log(double x);
5251
5252
/**
5253
 * Compute the natural logarithm of `x`.
5254
 *
5255
 * Domain: `0 < x <= INF`
5256
 *
5257
 * Range: `-INF <= y <= INF`
5258
 *
5259
 * It is an error for `x` to be less than or equal to 0.
5260
 *
5261
 * This function operates on single-precision floating point values, use
5262
 * SDL_log for double-precision floats.
5263
 *
5264
 * This function may use a different approximation across different versions,
5265
 * platforms and configurations. i.e, it can return a different value given
5266
 * the same input on different machines or operating systems, or if SDL is
5267
 * updated.
5268
 *
5269
 * \param x floating point value. Must be greater than 0.
5270
 * \returns the natural logarithm of `x`.
5271
 *
5272
 * \threadsafety It is safe to call this function from any thread.
5273
 *
5274
 * \since This function is available since SDL 3.2.0.
5275
 *
5276
 * \sa SDL_log
5277
 * \sa SDL_expf
5278
 */
5279
extern SDL_DECLSPEC float SDLCALL SDL_logf(float x);
5280
5281
/**
5282
 * Compute the base-10 logarithm of `x`.
5283
 *
5284
 * Domain: `0 < x <= INF`
5285
 *
5286
 * Range: `-INF <= y <= INF`
5287
 *
5288
 * It is an error for `x` to be less than or equal to 0.
5289
 *
5290
 * This function operates on double-precision floating point values, use
5291
 * SDL_log10f for single-precision floats.
5292
 *
5293
 * This function may use a different approximation across different versions,
5294
 * platforms and configurations. i.e, it can return a different value given
5295
 * the same input on different machines or operating systems, or if SDL is
5296
 * updated.
5297
 *
5298
 * \param x floating point value. Must be greater than 0.
5299
 * \returns the logarithm of `x`.
5300
 *
5301
 * \threadsafety It is safe to call this function from any thread.
5302
 *
5303
 * \since This function is available since SDL 3.2.0.
5304
 *
5305
 * \sa SDL_log10f
5306
 * \sa SDL_log
5307
 * \sa SDL_pow
5308
 */
5309
extern SDL_DECLSPEC double SDLCALL SDL_log10(double x);
5310
5311
/**
5312
 * Compute the base-10 logarithm of `x`.
5313
 *
5314
 * Domain: `0 < x <= INF`
5315
 *
5316
 * Range: `-INF <= y <= INF`
5317
 *
5318
 * It is an error for `x` to be less than or equal to 0.
5319
 *
5320
 * This function operates on single-precision floating point values, use
5321
 * SDL_log10 for double-precision floats.
5322
 *
5323
 * This function may use a different approximation across different versions,
5324
 * platforms and configurations. i.e, it can return a different value given
5325
 * the same input on different machines or operating systems, or if SDL is
5326
 * updated.
5327
 *
5328
 * \param x floating point value. Must be greater than 0.
5329
 * \returns the logarithm of `x`.
5330
 *
5331
 * \threadsafety It is safe to call this function from any thread.
5332
 *
5333
 * \since This function is available since SDL 3.2.0.
5334
 *
5335
 * \sa SDL_log10
5336
 * \sa SDL_logf
5337
 * \sa SDL_powf
5338
 */
5339
extern SDL_DECLSPEC float SDLCALL SDL_log10f(float x);
5340
5341
/**
5342
 * Split `x` into integer and fractional parts
5343
 *
5344
 * This function operates on double-precision floating point values, use
5345
 * SDL_modff for single-precision floats.
5346
 *
5347
 * \param x floating point value.
5348
 * \param y output pointer to store the integer part of `x`.
5349
 * \returns the fractional part of `x`.
5350
 *
5351
 * \threadsafety It is safe to call this function from any thread.
5352
 *
5353
 * \since This function is available since SDL 3.2.0.
5354
 *
5355
 * \sa SDL_modff
5356
 * \sa SDL_trunc
5357
 * \sa SDL_fmod
5358
 */
5359
extern SDL_DECLSPEC double SDLCALL SDL_modf(double x, double *y);
5360
5361
/**
5362
 * Split `x` into integer and fractional parts
5363
 *
5364
 * This function operates on single-precision floating point values, use
5365
 * SDL_modf for double-precision floats.
5366
 *
5367
 * \param x floating point value.
5368
 * \param y output pointer to store the integer part of `x`.
5369
 * \returns the fractional part of `x`.
5370
 *
5371
 * \threadsafety It is safe to call this function from any thread.
5372
 *
5373
 * \since This function is available since SDL 3.2.0.
5374
 *
5375
 * \sa SDL_modf
5376
 * \sa SDL_truncf
5377
 * \sa SDL_fmodf
5378
 */
5379
extern SDL_DECLSPEC float SDLCALL SDL_modff(float x, float *y);
5380
5381
/**
5382
 * Raise `x` to the power `y`
5383
 *
5384
 * Domain: `-INF <= x <= INF`, `-INF <= y <= INF`
5385
 *
5386
 * Range: `-INF <= z <= INF`
5387
 *
5388
 * If `y` is the base of the natural logarithm (e), consider using SDL_exp
5389
 * instead.
5390
 *
5391
 * This function operates on double-precision floating point values, use
5392
 * SDL_powf for single-precision floats.
5393
 *
5394
 * This function may use a different approximation across different versions,
5395
 * platforms and configurations. i.e, it can return a different value given
5396
 * the same input on different machines or operating systems, or if SDL is
5397
 * updated.
5398
 *
5399
 * \param x the base.
5400
 * \param y the exponent.
5401
 * \returns `x` raised to the power `y`.
5402
 *
5403
 * \threadsafety It is safe to call this function from any thread.
5404
 *
5405
 * \since This function is available since SDL 3.2.0.
5406
 *
5407
 * \sa SDL_powf
5408
 * \sa SDL_exp
5409
 * \sa SDL_log
5410
 */
5411
extern SDL_DECLSPEC double SDLCALL SDL_pow(double x, double y);
5412
5413
/**
5414
 * Raise `x` to the power `y`
5415
 *
5416
 * Domain: `-INF <= x <= INF`, `-INF <= y <= INF`
5417
 *
5418
 * Range: `-INF <= z <= INF`
5419
 *
5420
 * If `y` is the base of the natural logarithm (e), consider using SDL_exp
5421
 * instead.
5422
 *
5423
 * This function operates on single-precision floating point values, use
5424
 * SDL_pow for double-precision floats.
5425
 *
5426
 * This function may use a different approximation across different versions,
5427
 * platforms and configurations. i.e, it can return a different value given
5428
 * the same input on different machines or operating systems, or if SDL is
5429
 * updated.
5430
 *
5431
 * \param x the base.
5432
 * \param y the exponent.
5433
 * \returns `x` raised to the power `y`.
5434
 *
5435
 * \threadsafety It is safe to call this function from any thread.
5436
 *
5437
 * \since This function is available since SDL 3.2.0.
5438
 *
5439
 * \sa SDL_pow
5440
 * \sa SDL_expf
5441
 * \sa SDL_logf
5442
 */
5443
extern SDL_DECLSPEC float SDLCALL SDL_powf(float x, float y);
5444
5445
/**
5446
 * Round `x` to the nearest integer.
5447
 *
5448
 * Rounds `x` to the nearest integer. Values halfway between integers will be
5449
 * rounded away from zero.
5450
 *
5451
 * Domain: `-INF <= x <= INF`
5452
 *
5453
 * Range: `-INF <= y <= INF`, y integer
5454
 *
5455
 * This function operates on double-precision floating point values, use
5456
 * SDL_roundf for single-precision floats. To get the result as an integer
5457
 * type, use SDL_lround.
5458
 *
5459
 * \param x floating point value.
5460
 * \returns the nearest integer to `x`.
5461
 *
5462
 * \threadsafety It is safe to call this function from any thread.
5463
 *
5464
 * \since This function is available since SDL 3.2.0.
5465
 *
5466
 * \sa SDL_roundf
5467
 * \sa SDL_lround
5468
 * \sa SDL_floor
5469
 * \sa SDL_ceil
5470
 * \sa SDL_trunc
5471
 */
5472
extern SDL_DECLSPEC double SDLCALL SDL_round(double x);
5473
5474
/**
5475
 * Round `x` to the nearest integer.
5476
 *
5477
 * Rounds `x` to the nearest integer. Values halfway between integers will be
5478
 * rounded away from zero.
5479
 *
5480
 * Domain: `-INF <= x <= INF`
5481
 *
5482
 * Range: `-INF <= y <= INF`, y integer
5483
 *
5484
 * This function operates on single-precision floating point values, use
5485
 * SDL_round for double-precision floats. To get the result as an integer
5486
 * type, use SDL_lroundf.
5487
 *
5488
 * \param x floating point value.
5489
 * \returns the nearest integer to `x`.
5490
 *
5491
 * \threadsafety It is safe to call this function from any thread.
5492
 *
5493
 * \since This function is available since SDL 3.2.0.
5494
 *
5495
 * \sa SDL_round
5496
 * \sa SDL_lroundf
5497
 * \sa SDL_floorf
5498
 * \sa SDL_ceilf
5499
 * \sa SDL_truncf
5500
 */
5501
extern SDL_DECLSPEC float SDLCALL SDL_roundf(float x);
5502
5503
/**
5504
 * Round `x` to the nearest integer representable as a long
5505
 *
5506
 * Rounds `x` to the nearest integer. Values halfway between integers will be
5507
 * rounded away from zero.
5508
 *
5509
 * Domain: `-INF <= x <= INF`
5510
 *
5511
 * Range: `MIN_LONG <= y <= MAX_LONG`
5512
 *
5513
 * This function operates on double-precision floating point values, use
5514
 * SDL_lroundf for single-precision floats. To get the result as a
5515
 * floating-point type, use SDL_round.
5516
 *
5517
 * \param x floating point value.
5518
 * \returns the nearest integer to `x`.
5519
 *
5520
 * \threadsafety It is safe to call this function from any thread.
5521
 *
5522
 * \since This function is available since SDL 3.2.0.
5523
 *
5524
 * \sa SDL_lroundf
5525
 * \sa SDL_round
5526
 * \sa SDL_floor
5527
 * \sa SDL_ceil
5528
 * \sa SDL_trunc
5529
 */
5530
extern SDL_DECLSPEC long SDLCALL SDL_lround(double x);
5531
5532
/**
5533
 * Round `x` to the nearest integer representable as a long
5534
 *
5535
 * Rounds `x` to the nearest integer. Values halfway between integers will be
5536
 * rounded away from zero.
5537
 *
5538
 * Domain: `-INF <= x <= INF`
5539
 *
5540
 * Range: `MIN_LONG <= y <= MAX_LONG`
5541
 *
5542
 * This function operates on single-precision floating point values, use
5543
 * SDL_lround for double-precision floats. To get the result as a
5544
 * floating-point type, use SDL_roundf.
5545
 *
5546
 * \param x floating point value.
5547
 * \returns the nearest integer to `x`.
5548
 *
5549
 * \threadsafety It is safe to call this function from any thread.
5550
 *
5551
 * \since This function is available since SDL 3.2.0.
5552
 *
5553
 * \sa SDL_lround
5554
 * \sa SDL_roundf
5555
 * \sa SDL_floorf
5556
 * \sa SDL_ceilf
5557
 * \sa SDL_truncf
5558
 */
5559
extern SDL_DECLSPEC long SDLCALL SDL_lroundf(float x);
5560
5561
/**
5562
 * Scale `x` by an integer power of two.
5563
 *
5564
 * Multiplies `x` by the `n`th power of the floating point radix (always 2).
5565
 *
5566
 * Domain: `-INF <= x <= INF`, `n` integer
5567
 *
5568
 * Range: `-INF <= y <= INF`
5569
 *
5570
 * This function operates on double-precision floating point values, use
5571
 * SDL_scalbnf for single-precision floats.
5572
 *
5573
 * \param x floating point value to be scaled.
5574
 * \param n integer exponent.
5575
 * \returns `x * 2^n`.
5576
 *
5577
 * \threadsafety It is safe to call this function from any thread.
5578
 *
5579
 * \since This function is available since SDL 3.2.0.
5580
 *
5581
 * \sa SDL_scalbnf
5582
 * \sa SDL_pow
5583
 */
5584
extern SDL_DECLSPEC double SDLCALL SDL_scalbn(double x, int n);
5585
5586
/**
5587
 * Scale `x` by an integer power of two.
5588
 *
5589
 * Multiplies `x` by the `n`th power of the floating point radix (always 2).
5590
 *
5591
 * Domain: `-INF <= x <= INF`, `n` integer
5592
 *
5593
 * Range: `-INF <= y <= INF`
5594
 *
5595
 * This function operates on single-precision floating point values, use
5596
 * SDL_scalbn for double-precision floats.
5597
 *
5598
 * \param x floating point value to be scaled.
5599
 * \param n integer exponent.
5600
 * \returns `x * 2^n`.
5601
 *
5602
 * \threadsafety It is safe to call this function from any thread.
5603
 *
5604
 * \since This function is available since SDL 3.2.0.
5605
 *
5606
 * \sa SDL_scalbn
5607
 * \sa SDL_powf
5608
 */
5609
extern SDL_DECLSPEC float SDLCALL SDL_scalbnf(float x, int n);
5610
5611
/**
5612
 * Compute the sine of `x`.
5613
 *
5614
 * Domain: `-INF <= x <= INF`
5615
 *
5616
 * Range: `-1 <= y <= 1`
5617
 *
5618
 * This function operates on double-precision floating point values, use
5619
 * SDL_sinf for single-precision floats.
5620
 *
5621
 * This function may use a different approximation across different versions,
5622
 * platforms and configurations. i.e, it can return a different value given
5623
 * the same input on different machines or operating systems, or if SDL is
5624
 * updated.
5625
 *
5626
 * \param x floating point value, in radians.
5627
 * \returns sine of `x`.
5628
 *
5629
 * \threadsafety It is safe to call this function from any thread.
5630
 *
5631
 * \since This function is available since SDL 3.2.0.
5632
 *
5633
 * \sa SDL_sinf
5634
 * \sa SDL_asin
5635
 * \sa SDL_cos
5636
 */
5637
extern SDL_DECLSPEC double SDLCALL SDL_sin(double x);
5638
5639
/**
5640
 * Compute the sine of `x`.
5641
 *
5642
 * Domain: `-INF <= x <= INF`
5643
 *
5644
 * Range: `-1 <= y <= 1`
5645
 *
5646
 * This function operates on single-precision floating point values, use
5647
 * SDL_sin for double-precision floats.
5648
 *
5649
 * This function may use a different approximation across different versions,
5650
 * platforms and configurations. i.e, it can return a different value given
5651
 * the same input on different machines or operating systems, or if SDL is
5652
 * updated.
5653
 *
5654
 * \param x floating point value, in radians.
5655
 * \returns sine of `x`.
5656
 *
5657
 * \threadsafety It is safe to call this function from any thread.
5658
 *
5659
 * \since This function is available since SDL 3.2.0.
5660
 *
5661
 * \sa SDL_sin
5662
 * \sa SDL_asinf
5663
 * \sa SDL_cosf
5664
 */
5665
extern SDL_DECLSPEC float SDLCALL SDL_sinf(float x);
5666
5667
/**
5668
 * Compute the square root of `x`.
5669
 *
5670
 * Domain: `0 <= x <= INF`
5671
 *
5672
 * Range: `0 <= y <= INF`
5673
 *
5674
 * This function operates on double-precision floating point values, use
5675
 * SDL_sqrtf for single-precision floats.
5676
 *
5677
 * This function may use a different approximation across different versions,
5678
 * platforms and configurations. i.e, it can return a different value given
5679
 * the same input on different machines or operating systems, or if SDL is
5680
 * updated.
5681
 *
5682
 * \param x floating point value. Must be greater than or equal to 0.
5683
 * \returns square root of `x`.
5684
 *
5685
 * \threadsafety It is safe to call this function from any thread.
5686
 *
5687
 * \since This function is available since SDL 3.2.0.
5688
 *
5689
 * \sa SDL_sqrtf
5690
 */
5691
extern SDL_DECLSPEC double SDLCALL SDL_sqrt(double x);
5692
5693
/**
5694
 * Compute the square root of `x`.
5695
 *
5696
 * Domain: `0 <= x <= INF`
5697
 *
5698
 * Range: `0 <= y <= INF`
5699
 *
5700
 * This function operates on single-precision floating point values, use
5701
 * SDL_sqrt for double-precision floats.
5702
 *
5703
 * This function may use a different approximation across different versions,
5704
 * platforms and configurations. i.e, it can return a different value given
5705
 * the same input on different machines or operating systems, or if SDL is
5706
 * updated.
5707
 *
5708
 * \param x floating point value. Must be greater than or equal to 0.
5709
 * \returns square root of `x`.
5710
 *
5711
 * \threadsafety It is safe to call this function from any thread.
5712
 *
5713
 * \since This function is available since SDL 3.2.0.
5714
 *
5715
 * \sa SDL_sqrt
5716
 */
5717
extern SDL_DECLSPEC float SDLCALL SDL_sqrtf(float x);
5718
5719
/**
5720
 * Compute the tangent of `x`.
5721
 *
5722
 * Domain: `-INF <= x <= INF`
5723
 *
5724
 * Range: `-INF <= y <= INF`
5725
 *
5726
 * This function operates on double-precision floating point values, use
5727
 * SDL_tanf for single-precision floats.
5728
 *
5729
 * This function may use a different approximation across different versions,
5730
 * platforms and configurations. i.e, it can return a different value given
5731
 * the same input on different machines or operating systems, or if SDL is
5732
 * updated.
5733
 *
5734
 * \param x floating point value, in radians.
5735
 * \returns tangent of `x`.
5736
 *
5737
 * \threadsafety It is safe to call this function from any thread.
5738
 *
5739
 * \since This function is available since SDL 3.2.0.
5740
 *
5741
 * \sa SDL_tanf
5742
 * \sa SDL_sin
5743
 * \sa SDL_cos
5744
 * \sa SDL_atan
5745
 * \sa SDL_atan2
5746
 */
5747
extern SDL_DECLSPEC double SDLCALL SDL_tan(double x);
5748
5749
/**
5750
 * Compute the tangent of `x`.
5751
 *
5752
 * Domain: `-INF <= x <= INF`
5753
 *
5754
 * Range: `-INF <= y <= INF`
5755
 *
5756
 * This function operates on single-precision floating point values, use
5757
 * SDL_tan for double-precision floats.
5758
 *
5759
 * This function may use a different approximation across different versions,
5760
 * platforms and configurations. i.e, it can return a different value given
5761
 * the same input on different machines or operating systems, or if SDL is
5762
 * updated.
5763
 *
5764
 * \param x floating point value, in radians.
5765
 * \returns tangent of `x`.
5766
 *
5767
 * \threadsafety It is safe to call this function from any thread.
5768
 *
5769
 * \since This function is available since SDL 3.2.0.
5770
 *
5771
 * \sa SDL_tan
5772
 * \sa SDL_sinf
5773
 * \sa SDL_cosf
5774
 * \sa SDL_atanf
5775
 * \sa SDL_atan2f
5776
 */
5777
extern SDL_DECLSPEC float SDLCALL SDL_tanf(float x);
5778
5779
/**
5780
 * An opaque handle representing string encoding conversion state.
5781
 *
5782
 * \since This datatype is available since SDL 3.2.0.
5783
 *
5784
 * \sa SDL_iconv_open
5785
 */
5786
typedef struct SDL_iconv_data_t *SDL_iconv_t;
5787
5788
/**
5789
 * This function allocates a context for the specified character set
5790
 * conversion.
5791
 *
5792
 * \param tocode The target character encoding, must not be NULL.
5793
 * \param fromcode The source character encoding, must not be NULL.
5794
 * \returns a handle that must be freed with SDL_iconv_close, or
5795
 *          SDL_ICONV_ERROR on failure.
5796
 *
5797
 * \since This function is available since SDL 3.2.0.
5798
 *
5799
 * \sa SDL_iconv
5800
 * \sa SDL_iconv_close
5801
 * \sa SDL_iconv_string
5802
 */
5803
extern SDL_DECLSPEC SDL_iconv_t SDLCALL SDL_iconv_open(const char *tocode,
5804
                                                   const char *fromcode);
5805
5806
/**
5807
 * This function frees a context used for character set conversion.
5808
 *
5809
 * \param cd The character set conversion handle.
5810
 * \returns 0 on success, or -1 on failure.
5811
 *
5812
 * \since This function is available since SDL 3.2.0.
5813
 *
5814
 * \sa SDL_iconv
5815
 * \sa SDL_iconv_open
5816
 * \sa SDL_iconv_string
5817
 */
5818
extern SDL_DECLSPEC int SDLCALL SDL_iconv_close(SDL_iconv_t cd);
5819
5820
/**
5821
 * This function converts text between encodings, reading from and writing to
5822
 * a buffer.
5823
 *
5824
 * It returns the number of successful conversions on success. On error,
5825
 * SDL_ICONV_E2BIG is returned when the output buffer is too small, or
5826
 * SDL_ICONV_EILSEQ is returned when an invalid input sequence is encountered,
5827
 * or SDL_ICONV_EINVAL is returned when an incomplete input sequence is
5828
 * encountered.
5829
 *
5830
 * On exit:
5831
 *
5832
 * - inbuf will point to the beginning of the next multibyte sequence. On
5833
 *   error, this is the location of the problematic input sequence. On
5834
 *   success, this is the end of the input sequence.
5835
 * - inbytesleft will be set to the number of bytes left to convert, which
5836
 *   will be 0 on success.
5837
 * - outbuf will point to the location where to store the next output byte.
5838
 * - outbytesleft will be set to the number of bytes left in the output
5839
 *   buffer.
5840
 *
5841
 * \param cd The character set conversion context, created in
5842
 *           SDL_iconv_open().
5843
 * \param inbuf Address of variable that points to the first character of the
5844
 *              input sequence.
5845
 * \param inbytesleft The number of bytes in the input buffer.
5846
 * \param outbuf Address of variable that points to the output buffer.
5847
 * \param outbytesleft The number of bytes in the output buffer.
5848
 * \returns the number of conversions on success, or a negative error code.
5849
 *
5850
 * \since This function is available since SDL 3.2.0.
5851
 *
5852
 * \sa SDL_iconv_open
5853
 * \sa SDL_iconv_close
5854
 * \sa SDL_iconv_string
5855
 */
5856
extern SDL_DECLSPEC size_t SDLCALL SDL_iconv(SDL_iconv_t cd, const char **inbuf,
5857
                                         size_t *inbytesleft, char **outbuf,
5858
                                         size_t *outbytesleft);
5859
5860
#define SDL_ICONV_ERROR     (size_t)-1  /**< Generic error. Check SDL_GetError()? */
5861
#define SDL_ICONV_E2BIG     (size_t)-2  /**< Output buffer was too small. */
5862
#define SDL_ICONV_EILSEQ    (size_t)-3  /**< Invalid input sequence was encountered. */
5863
#define SDL_ICONV_EINVAL    (size_t)-4  /**< Incomplete input sequence was encountered. */
5864
5865
5866
/**
5867
 * Helper function to convert a string's encoding in one call.
5868
 *
5869
 * This function converts a buffer or string between encodings in one pass.
5870
 *
5871
 * The string does not need to be NULL-terminated; this function operates on
5872
 * the number of bytes specified in `inbytesleft` whether there is a NULL
5873
 * character anywhere in the buffer.
5874
 *
5875
 * The returned string is owned by the caller, and should be passed to
5876
 * SDL_free when no longer needed.
5877
 *
5878
 * \param tocode the character encoding of the output string. Examples are
5879
 *               "UTF-8", "UCS-4", etc.
5880
 * \param fromcode the character encoding of data in `inbuf`.
5881
 * \param inbuf the string to convert to a different encoding.
5882
 * \param inbytesleft the size of the input string _in bytes_.
5883
 * \returns a new string, converted to the new encoding, or NULL on error.
5884
 *
5885
 * \since This function is available since SDL 3.2.0.
5886
 *
5887
 * \sa SDL_iconv_open
5888
 * \sa SDL_iconv_close
5889
 * \sa SDL_iconv
5890
 */
5891
extern SDL_DECLSPEC char * SDLCALL SDL_iconv_string(const char *tocode,
5892
                                               const char *fromcode,
5893
                                               const char *inbuf,
5894
                                               size_t inbytesleft);
5895
5896
/* Some helper macros for common SDL_iconv_string cases... */
5897
5898
/**
5899
 * Convert a UTF-8 string to the current locale's character encoding.
5900
 *
5901
 * This is a helper macro that might be more clear than calling
5902
 * SDL_iconv_string directly. However, it double-evaluates its parameter, so
5903
 * do not use an expression with side-effects here.
5904
 *
5905
 * \param S the string to convert.
5906
 * \returns a new string, converted to the new encoding, or NULL on error.
5907
 *
5908
 * \since This macro is available since SDL 3.2.0.
5909
 */
5910
#define SDL_iconv_utf8_locale(S)    SDL_iconv_string("", "UTF-8", S, SDL_strlen(S)+1)
5911
5912
/**
5913
 * Convert a UTF-8 string to UCS-2.
5914
 *
5915
 * This is a helper macro that might be more clear than calling
5916
 * SDL_iconv_string directly. However, it double-evaluates its parameter, so
5917
 * do not use an expression with side-effects here.
5918
 *
5919
 * \param S the string to convert.
5920
 * \returns a new string, converted to the new encoding, or NULL on error.
5921
 *
5922
 * \since This macro is available since SDL 3.2.0.
5923
 */
5924
#define SDL_iconv_utf8_ucs2(S)      (Uint16 *)SDL_iconv_string("UCS-2", "UTF-8", S, SDL_strlen(S)+1)
5925
5926
/**
5927
 * Convert a UTF-8 string to UCS-4.
5928
 *
5929
 * This is a helper macro that might be more clear than calling
5930
 * SDL_iconv_string directly. However, it double-evaluates its parameter, so
5931
 * do not use an expression with side-effects here.
5932
 *
5933
 * \param S the string to convert.
5934
 * \returns a new string, converted to the new encoding, or NULL on error.
5935
 *
5936
 * \since This macro is available since SDL 3.2.0.
5937
 */
5938
#define SDL_iconv_utf8_ucs4(S)      (Uint32 *)SDL_iconv_string("UCS-4", "UTF-8", S, SDL_strlen(S)+1)
5939
5940
/**
5941
 * Convert a wchar_t string to UTF-8.
5942
 *
5943
 * This is a helper macro that might be more clear than calling
5944
 * SDL_iconv_string directly. However, it double-evaluates its parameter, so
5945
 * do not use an expression with side-effects here.
5946
 *
5947
 * \param S the string to convert.
5948
 * \returns a new string, converted to the new encoding, or NULL on error.
5949
 *
5950
 * \since This macro is available since SDL 3.2.0.
5951
 */
5952
#define SDL_iconv_wchar_utf8(S)     SDL_iconv_string("UTF-8", "WCHAR_T", (char *)S, (SDL_wcslen(S)+1)*sizeof(wchar_t))
5953
5954
5955
/* force builds using Clang's static analysis tools to use literal C runtime
5956
   here, since there are possibly tests that are ineffective otherwise. */
5957
#if defined(__clang_analyzer__) && !defined(SDL_DISABLE_ANALYZE_MACROS)
5958
5959
/* The analyzer knows about strlcpy even when the system doesn't provide it */
5960
#if !defined(HAVE_STRLCPY) && !defined(strlcpy)
5961
size_t strlcpy(char *dst, const char *src, size_t size);
5962
#endif
5963
5964
/* The analyzer knows about strlcat even when the system doesn't provide it */
5965
#if !defined(HAVE_STRLCAT) && !defined(strlcat)
5966
size_t strlcat(char *dst, const char *src, size_t size);
5967
#endif
5968
5969
#if !defined(HAVE_WCSLCPY) && !defined(wcslcpy)
5970
size_t wcslcpy(wchar_t *dst, const wchar_t *src, size_t size);
5971
#endif
5972
5973
#if !defined(HAVE_WCSLCAT) && !defined(wcslcat)
5974
size_t wcslcat(wchar_t *dst, const wchar_t *src, size_t size);
5975
#endif
5976
5977
#ifndef _WIN32
5978
/* strdup is not ANSI but POSIX, and its prototype might be hidden... */
5979
/* not for windows: might conflict with string.h where strdup may have
5980
 * dllimport attribute: https://github.com/libsdl-org/SDL/issues/12948 */
5981
char *strdup(const char *str);
5982
#endif
5983
5984
/* Starting LLVM 16, the analyser errors out if these functions do not have
5985
   their prototype defined (clang-diagnostic-implicit-function-declaration) */
5986
#include <stdio.h>
5987
#include <stdlib.h>
5988
5989
#define SDL_malloc malloc
5990
#define SDL_calloc calloc
5991
#define SDL_realloc realloc
5992
#define SDL_free free
5993
#ifndef SDL_memcpy
5994
#define SDL_memcpy memcpy
5995
#endif
5996
#ifndef SDL_memmove
5997
#define SDL_memmove memmove
5998
#endif
5999
#ifndef SDL_memset
6000
#define SDL_memset memset
6001
#endif
6002
#define SDL_memcmp memcmp
6003
#define SDL_strlcpy strlcpy
6004
#define SDL_strlcat strlcat
6005
#define SDL_strlen strlen
6006
#define SDL_wcslen wcslen
6007
#define SDL_wcslcpy wcslcpy
6008
#define SDL_wcslcat wcslcat
6009
#define SDL_strdup strdup
6010
#define SDL_wcsdup wcsdup
6011
#define SDL_strchr strchr
6012
#define SDL_strrchr strrchr
6013
#define SDL_strstr strstr
6014
#define SDL_wcsstr wcsstr
6015
#define SDL_strtok_r strtok_r
6016
#define SDL_strcmp strcmp
6017
#define SDL_wcscmp wcscmp
6018
#define SDL_strncmp strncmp
6019
#define SDL_wcsncmp wcsncmp
6020
#define SDL_strcasecmp strcasecmp
6021
#define SDL_strncasecmp strncasecmp
6022
#define SDL_strpbrk strpbrk
6023
#define SDL_sscanf sscanf
6024
#define SDL_vsscanf vsscanf
6025
#define SDL_snprintf snprintf
6026
#define SDL_vsnprintf vsnprintf
6027
#endif
6028
6029
/**
6030
 * Multiply two integers, checking for overflow.
6031
 *
6032
 * If `a * b` would overflow, return false.
6033
 *
6034
 * Otherwise store `a * b` via ret and return true.
6035
 *
6036
 * \param a the multiplicand.
6037
 * \param b the multiplier.
6038
 * \param ret on non-overflow output, stores the multiplication result, may
6039
 *            not be NULL.
6040
 * \returns false on overflow, true if result is multiplied without overflow.
6041
 *
6042
 * \threadsafety It is safe to call this function from any thread.
6043
 *
6044
 * \since This function is available since SDL 3.2.0.
6045
 */
6046
SDL_FORCE_INLINE bool SDL_size_mul_check_overflow(size_t a, size_t b, size_t *ret)
6047
0
{
6048
0
    if (a != 0 && b > SDL_SIZE_MAX / a) {
6049
0
        return false;
6050
0
    }
6051
0
    *ret = a * b;
6052
0
    return true;
6053
0
}
Unexecuted instantiation: emulator.cpp:_ZL27SDL_size_mul_check_overflowmmPm
Unexecuted instantiation: sound.cpp:_ZL27SDL_size_mul_check_overflowmmPm
Unexecuted instantiation: ay8912.cpp:_ZL27SDL_size_mul_check_overflowmmPm
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL27SDL_size_mul_check_overflowmmPm
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL27SDL_size_mul_check_overflowmmPm
6054
6055
#ifndef SDL_WIKI_DOCUMENTATION_SECTION
6056
#if SDL_HAS_BUILTIN(__builtin_mul_overflow)
6057
/* This needs to be wrapped in an inline rather than being a direct #define,
6058
 * because __builtin_mul_overflow() is type-generic, but we want to be
6059
 * consistent about interpreting a and b as size_t. */
6060
SDL_FORCE_INLINE bool SDL_size_mul_check_overflow_builtin(size_t a, size_t b, size_t *ret)
6061
0
{
6062
0
    return (__builtin_mul_overflow(a, b, ret) == 0);
6063
0
}
Unexecuted instantiation: emulator.cpp:_ZL35SDL_size_mul_check_overflow_builtinmmPm
Unexecuted instantiation: sound.cpp:_ZL35SDL_size_mul_check_overflow_builtinmmPm
Unexecuted instantiation: ay8912.cpp:_ZL35SDL_size_mul_check_overflow_builtinmmPm
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL35SDL_size_mul_check_overflow_builtinmmPm
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL35SDL_size_mul_check_overflow_builtinmmPm
6064
#define SDL_size_mul_check_overflow(a, b, ret) SDL_size_mul_check_overflow_builtin(a, b, ret)
6065
#endif
6066
#endif
6067
6068
/**
6069
 * Add two integers, checking for overflow.
6070
 *
6071
 * If `a + b` would overflow, return false.
6072
 *
6073
 * Otherwise store `a + b` via ret and return true.
6074
 *
6075
 * \param a the first addend.
6076
 * \param b the second addend.
6077
 * \param ret on non-overflow output, stores the addition result, may not be
6078
 *            NULL.
6079
 * \returns false on overflow, true if result is added without overflow.
6080
 *
6081
 * \threadsafety It is safe to call this function from any thread.
6082
 *
6083
 * \since This function is available since SDL 3.2.0.
6084
 */
6085
SDL_FORCE_INLINE bool SDL_size_add_check_overflow(size_t a, size_t b, size_t *ret)
6086
0
{
6087
0
    if (b > SDL_SIZE_MAX - a) {
6088
0
        return false;
6089
0
    }
6090
0
    *ret = a + b;
6091
0
    return true;
6092
0
}
Unexecuted instantiation: emulator.cpp:_ZL27SDL_size_add_check_overflowmmPm
Unexecuted instantiation: sound.cpp:_ZL27SDL_size_add_check_overflowmmPm
Unexecuted instantiation: ay8912.cpp:_ZL27SDL_size_add_check_overflowmmPm
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL27SDL_size_add_check_overflowmmPm
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL27SDL_size_add_check_overflowmmPm
6093
6094
#ifndef SDL_WIKI_DOCUMENTATION_SECTION
6095
#if SDL_HAS_BUILTIN(__builtin_add_overflow)
6096
/* This needs to be wrapped in an inline rather than being a direct #define,
6097
 * the same as the call to __builtin_mul_overflow() above. */
6098
SDL_FORCE_INLINE bool SDL_size_add_check_overflow_builtin(size_t a, size_t b, size_t *ret)
6099
0
{
6100
0
    return (__builtin_add_overflow(a, b, ret) == 0);
6101
0
}
Unexecuted instantiation: emulator.cpp:_ZL35SDL_size_add_check_overflow_builtinmmPm
Unexecuted instantiation: sound.cpp:_ZL35SDL_size_add_check_overflow_builtinmmPm
Unexecuted instantiation: ay8912.cpp:_ZL35SDL_size_add_check_overflow_builtinmmPm
Unexecuted instantiation: imgui_impl_sdl3.cpp:_ZL35SDL_size_add_check_overflow_builtinmmPm
Unexecuted instantiation: imgui_impl_sdlrenderer3.cpp:_ZL35SDL_size_add_check_overflow_builtinmmPm
6102
#define SDL_size_add_check_overflow(a, b, ret) SDL_size_add_check_overflow_builtin(a, b, ret)
6103
#endif
6104
#endif
6105
6106
/* This is a generic function pointer which should be cast to the type you expect */
6107
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
6108
6109
/**
6110
 * A generic function pointer.
6111
 *
6112
 * In theory, generic function pointers should use this, instead of `void *`,
6113
 * since some platforms could treat code addresses differently than data
6114
 * addresses. Although in current times no popular platforms make this
6115
 * distinction, it is more correct and portable to use the correct type for a
6116
 * generic pointer.
6117
 *
6118
 * If for some reason you need to force this typedef to be an actual `void *`,
6119
 * perhaps to work around a compiler or existing code, you can define
6120
 * `SDL_FUNCTION_POINTER_IS_VOID_POINTER` before including any SDL headers.
6121
 *
6122
 * \since This datatype is available since SDL 3.2.0.
6123
 */
6124
typedef void (*SDL_FunctionPointer)(void);
6125
#elif defined(SDL_FUNCTION_POINTER_IS_VOID_POINTER)
6126
typedef void *SDL_FunctionPointer;
6127
#else
6128
typedef void (*SDL_FunctionPointer)(void);
6129
#endif
6130
6131
/* Ends C function definitions when using C++ */
6132
#ifdef __cplusplus
6133
}
6134
#endif
6135
#include <SDL3/SDL_close_code.h>
6136
6137
#endif /* SDL_stdinc_h_ */
/opt/homebrew/include/SDL3/SDL_touch.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryTouch
24
 *
25
 * SDL offers touch input, on platforms that support it. It can manage
26
 * multiple touch devices and track multiple fingers on those devices.
27
 *
28
 * Touches are mostly dealt with through the event system, in the
29
 * SDL_EVENT_FINGER_DOWN, SDL_EVENT_FINGER_MOTION, and SDL_EVENT_FINGER_UP
30
 * events, but there are also functions to query for hardware details, etc.
31
 *
32
 * The touch system, by default, will also send virtual mouse events; this can
33
 * be useful for making a some desktop apps work on a phone without
34
 * significant changes. For apps that care about mouse and touch input
35
 * separately, they should ignore mouse events that have a `which` field of
36
 * SDL_TOUCH_MOUSEID.
37
 */
38
39
#ifndef SDL_touch_h_
40
#define SDL_touch_h_
41
42
#include <SDL3/SDL_stdinc.h>
43
#include <SDL3/SDL_error.h>
44
#include <SDL3/SDL_mouse.h>
45
46
#include <SDL3/SDL_begin_code.h>
47
/* Set up for C function definitions, even when using C++ */
48
#ifdef __cplusplus
49
extern "C" {
50
#endif
51
52
/**
53
 * A unique ID for a touch device.
54
 *
55
 * This ID is valid for the time the device is connected to the system, and is
56
 * never reused for the lifetime of the application.
57
 *
58
 * The value 0 is an invalid ID.
59
 *
60
 * \since This datatype is available since SDL 3.2.0.
61
 */
62
typedef Uint64 SDL_TouchID;
63
64
/**
65
 * A unique ID for a single finger on a touch device.
66
 *
67
 * This ID is valid for the time the finger (stylus, etc) is touching and will
68
 * be unique for all fingers currently in contact, so this ID tracks the
69
 * lifetime of a single continuous touch. This value may represent an index, a
70
 * pointer, or some other unique ID, depending on the platform.
71
 *
72
 * The value 0 is an invalid ID.
73
 *
74
 * \since This datatype is available since SDL 3.2.0.
75
 */
76
typedef Uint64 SDL_FingerID;
77
78
/**
79
 * An enum that describes the type of a touch device.
80
 *
81
 * \since This enum is available since SDL 3.2.0.
82
 */
83
typedef enum SDL_TouchDeviceType
84
{
85
    SDL_TOUCH_DEVICE_INVALID = -1,
86
    SDL_TOUCH_DEVICE_DIRECT,            /**< touch screen with window-relative coordinates */
87
    SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE, /**< trackpad with absolute device coordinates */
88
    SDL_TOUCH_DEVICE_INDIRECT_RELATIVE  /**< trackpad with screen cursor-relative coordinates */
89
} SDL_TouchDeviceType;
90
91
/**
92
 * Data about a single finger in a multitouch event.
93
 *
94
 * Each touch event is a collection of fingers that are simultaneously in
95
 * contact with the touch device (so a "touch" can be a "multitouch," in
96
 * reality), and this struct reports details of the specific fingers.
97
 *
98
 * \since This struct is available since SDL 3.2.0.
99
 *
100
 * \sa SDL_GetTouchFingers
101
 */
102
typedef struct SDL_Finger
103
{
104
    SDL_FingerID id;  /**< the finger ID */
105
    float x;  /**< the x-axis location of the touch event, normalized (0...1) */
106
    float y;  /**< the y-axis location of the touch event, normalized (0...1) */
107
    float pressure; /**< the quantity of pressure applied, normalized (0...1) */
108
} SDL_Finger;
109
110
/**
111
 * The SDL_MouseID for mouse events simulated with touch input.
112
 *
113
 * \since This macro is available since SDL 3.2.0.
114
 */
115
128
#define SDL_TOUCH_MOUSEID ((SDL_MouseID)-1)
116
117
/**
118
 * The SDL_TouchID for touch events simulated with mouse input.
119
 *
120
 * \since This macro is available since SDL 3.2.0.
121
 */
122
#define SDL_MOUSE_TOUCHID ((SDL_TouchID)-1)
123
124
125
/**
126
 * Get a list of registered touch devices.
127
 *
128
 * On some platforms SDL first sees the touch device if it was actually used.
129
 * Therefore the returned list might be empty, although devices are available.
130
 * After using all devices at least once the number will be correct.
131
 *
132
 * \param count a pointer filled in with the number of devices returned, may
133
 *              be NULL.
134
 * \returns a 0 terminated array of touch device IDs or NULL on failure; call
135
 *          SDL_GetError() for more information. This should be freed with
136
 *          SDL_free() when it is no longer needed.
137
 *
138
 * \since This function is available since SDL 3.2.0.
139
 */
140
extern SDL_DECLSPEC SDL_TouchID * SDLCALL SDL_GetTouchDevices(int *count);
141
142
/**
143
 * Get the touch device name as reported from the driver.
144
 *
145
 * \param touchID the touch device instance ID.
146
 * \returns touch device name, or NULL on failure; call SDL_GetError() for
147
 *          more information.
148
 *
149
 * \since This function is available since SDL 3.2.0.
150
 */
151
extern SDL_DECLSPEC const char * SDLCALL SDL_GetTouchDeviceName(SDL_TouchID touchID);
152
153
/**
154
 * Get the type of the given touch device.
155
 *
156
 * \param touchID the ID of a touch device.
157
 * \returns touch device type.
158
 *
159
 * \since This function is available since SDL 3.2.0.
160
 */
161
extern SDL_DECLSPEC SDL_TouchDeviceType SDLCALL SDL_GetTouchDeviceType(SDL_TouchID touchID);
162
163
/**
164
 * Get a list of active fingers for a given touch device.
165
 *
166
 * \param touchID the ID of a touch device.
167
 * \param count a pointer filled in with the number of fingers returned, can
168
 *              be NULL.
169
 * \returns a NULL terminated array of SDL_Finger pointers or NULL on failure;
170
 *          call SDL_GetError() for more information. This is a single
171
 *          allocation that should be freed with SDL_free() when it is no
172
 *          longer needed.
173
 *
174
 * \since This function is available since SDL 3.2.0.
175
 */
176
extern SDL_DECLSPEC SDL_Finger ** SDLCALL SDL_GetTouchFingers(SDL_TouchID touchID, int *count);
177
178
/* Ends C function definitions when using C++ */
179
#ifdef __cplusplus
180
}
181
#endif
182
#include <SDL3/SDL_close_code.h>
183
184
#endif /* SDL_touch_h_ */
/opt/homebrew/include/SDL3/SDL_version.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryVersion
24
 *
25
 * Functionality to query the current SDL version, both as headers the app was
26
 * compiled against, and a library the app is linked to.
27
 */
28
29
#ifndef SDL_version_h_
30
#define SDL_version_h_
31
32
#include <SDL3/SDL_stdinc.h>
33
34
#include <SDL3/SDL_begin_code.h>
35
/* Set up for C function definitions, even when using C++ */
36
#ifdef __cplusplus
37
extern "C" {
38
#endif
39
40
/**
41
 * The current major version of SDL headers.
42
 *
43
 * If this were SDL version 3.2.1, this value would be 3.
44
 *
45
 * \since This macro is available since SDL 3.2.0.
46
 */
47
1
#define SDL_MAJOR_VERSION   3
48
49
/**
50
 * The current minor version of the SDL headers.
51
 *
52
 * If this were SDL version 3.2.1, this value would be 2.
53
 *
54
 * \since This macro is available since SDL 3.2.0.
55
 */
56
1
#define SDL_MINOR_VERSION   2
57
58
/**
59
 * The current micro (or patchlevel) version of the SDL headers.
60
 *
61
 * If this were SDL version 3.2.1, this value would be 1.
62
 *
63
 * \since This macro is available since SDL 3.2.0.
64
 */
65
1
#define SDL_MICRO_VERSION   24
66
67
/**
68
 * This macro turns the version numbers into a numeric value.
69
 *
70
 * (1,2,3) becomes 1002003.
71
 *
72
 * \param major the major version number.
73
 * \param minor the minorversion number.
74
 * \param patch the patch version number.
75
 *
76
 * \since This macro is available since SDL 3.2.0.
77
 */
78
#define SDL_VERSIONNUM(major, minor, patch) \
79
    ((major) * 1000000 + (minor) * 1000 + (patch))
80
81
/**
82
 * This macro extracts the major version from a version number
83
 *
84
 * 1002003 becomes 1.
85
 *
86
 * \param version the version number.
87
 *
88
 * \since This macro is available since SDL 3.2.0.
89
 */
90
1
#define SDL_VERSIONNUM_MAJOR(version) ((version) / 1000000)
91
92
/**
93
 * This macro extracts the minor version from a version number
94
 *
95
 * 1002003 becomes 2.
96
 *
97
 * \param version the version number.
98
 *
99
 * \since This macro is available since SDL 3.2.0.
100
 */
101
1
#define SDL_VERSIONNUM_MINOR(version) (((version) / 1000) % 1000)
102
103
/**
104
 * This macro extracts the micro version from a version number
105
 *
106
 * 1002003 becomes 3.
107
 *
108
 * \param version the version number.
109
 *
110
 * \since This macro is available since SDL 3.2.0.
111
 */
112
1
#define SDL_VERSIONNUM_MICRO(version) ((version) % 1000)
113
114
/**
115
 * This is the version number macro for the current SDL version.
116
 *
117
 * \since This macro is available since SDL 3.2.0.
118
 *
119
 * \sa SDL_GetVersion
120
 */
121
#define SDL_VERSION \
122
    SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION)
123
124
/**
125
 * This macro will evaluate to true if compiled with SDL at least X.Y.Z.
126
 *
127
 * \since This macro is available since SDL 3.2.0.
128
 */
129
#define SDL_VERSION_ATLEAST(X, Y, Z) \
130
    (SDL_VERSION >= SDL_VERSIONNUM(X, Y, Z))
131
132
/**
133
 * Get the version of SDL that is linked against your program.
134
 *
135
 * If you are linking to SDL dynamically, then it is possible that the current
136
 * version will be different than the version you compiled against. This
137
 * function returns the current version, while SDL_VERSION is the version you
138
 * compiled with.
139
 *
140
 * This function may be called safely at any time, even before SDL_Init().
141
 *
142
 * \returns the version of the linked library.
143
 *
144
 * \since This function is available since SDL 3.2.0.
145
 *
146
 * \sa SDL_GetRevision
147
 */
148
extern SDL_DECLSPEC int SDLCALL SDL_GetVersion(void);
149
150
/**
151
 * Get the code revision of SDL that is linked against your program.
152
 *
153
 * This value is the revision of the code you are linked with and may be
154
 * different from the code you are compiling with, which is found in the
155
 * constant SDL_REVISION.
156
 *
157
 * The revision is arbitrary string (a hash value) uniquely identifying the
158
 * exact revision of the SDL library in use, and is only useful in comparing
159
 * against other revisions. It is NOT an incrementing number.
160
 *
161
 * If SDL wasn't built from a git repository with the appropriate tools, this
162
 * will return an empty string.
163
 *
164
 * You shouldn't use this function for anything but logging it for debugging
165
 * purposes. The string is not intended to be reliable in any way.
166
 *
167
 * \returns an arbitrary string, uniquely identifying the exact revision of
168
 *          the SDL library in use.
169
 *
170
 * \since This function is available since SDL 3.2.0.
171
 *
172
 * \sa SDL_GetVersion
173
 */
174
extern SDL_DECLSPEC const char * SDLCALL SDL_GetRevision(void);
175
176
177
/* Ends C function definitions when using C++ */
178
#ifdef __cplusplus
179
}
180
#endif
181
#include <SDL3/SDL_close_code.h>
182
183
#endif /* SDL_version_h_ */
/opt/homebrew/include/SDL3/SDL_video.h
Line
Count
Source
1
/*
2
  Simple DirectMedia Layer
3
  Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5
  This software is provided 'as-is', without any express or implied
6
  warranty.  In no event will the authors be held liable for any damages
7
  arising from the use of this software.
8
9
  Permission is granted to anyone to use this software for any purpose,
10
  including commercial applications, and to alter it and redistribute it
11
  freely, subject to the following restrictions:
12
13
  1. The origin of this software must not be misrepresented; you must not
14
     claim that you wrote the original software. If you use this software
15
     in a product, an acknowledgment in the product documentation would be
16
     appreciated but is not required.
17
  2. Altered source versions must be plainly marked as such, and must not be
18
     misrepresented as being the original software.
19
  3. This notice may not be removed or altered from any source distribution.
20
*/
21
22
/**
23
 * # CategoryVideo
24
 *
25
 * SDL's video subsystem is largely interested in abstracting window
26
 * management from the underlying operating system. You can create windows,
27
 * manage them in various ways, set them fullscreen, and get events when
28
 * interesting things happen with them, such as the mouse or keyboard
29
 * interacting with a window.
30
 *
31
 * The video subsystem is also interested in abstracting away some
32
 * platform-specific differences in OpenGL: context creation, swapping
33
 * buffers, etc. This may be crucial to your app, but also you are not
34
 * required to use OpenGL at all. In fact, SDL can provide rendering to those
35
 * windows as well, either with an easy-to-use
36
 * [2D API](https://wiki.libsdl.org/SDL3/CategoryRender)
37
 * or with a more-powerful
38
 * [GPU API](https://wiki.libsdl.org/SDL3/CategoryGPU)
39
 * . Of course, it can simply get out of your way and give you the window
40
 * handles you need to use Vulkan, Direct3D, Metal, or whatever else you like
41
 * directly, too.
42
 *
43
 * The video subsystem covers a lot of functionality, out of necessity, so it
44
 * is worth perusing the list of functions just to see what's available, but
45
 * most apps can get by with simply creating a window and listening for
46
 * events, so start with SDL_CreateWindow() and SDL_PollEvent().
47
 */
48
49
#ifndef SDL_video_h_
50
#define SDL_video_h_
51
52
#include <SDL3/SDL_stdinc.h>
53
#include <SDL3/SDL_error.h>
54
#include <SDL3/SDL_pixels.h>
55
#include <SDL3/SDL_properties.h>
56
#include <SDL3/SDL_rect.h>
57
#include <SDL3/SDL_surface.h>
58
59
#include <SDL3/SDL_begin_code.h>
60
/* Set up for C function definitions, even when using C++ */
61
#ifdef __cplusplus
62
extern "C" {
63
#endif
64
65
/**
66
 * This is a unique ID for a display for the time it is connected to the
67
 * system, and is never reused for the lifetime of the application.
68
 *
69
 * If the display is disconnected and reconnected, it will get a new ID.
70
 *
71
 * The value 0 is an invalid ID.
72
 *
73
 * \since This datatype is available since SDL 3.2.0.
74
 */
75
typedef Uint32 SDL_DisplayID;
76
77
/**
78
 * This is a unique ID for a window.
79
 *
80
 * The value 0 is an invalid ID.
81
 *
82
 * \since This datatype is available since SDL 3.2.0.
83
 */
84
typedef Uint32 SDL_WindowID;
85
86
/* Global video properties... */
87
88
/**
89
 * The pointer to the global `wl_display` object used by the Wayland video
90
 * backend.
91
 *
92
 * Can be set before the video subsystem is initialized to import an external
93
 * `wl_display` object from an application or toolkit for use in SDL, or read
94
 * after initialization to export the `wl_display` used by the Wayland video
95
 * backend. Setting this property after the video subsystem has been
96
 * initialized has no effect, and reading it when the video subsystem is
97
 * uninitialized will either return the user provided value, if one was set
98
 * prior to initialization, or NULL. See docs/README-wayland.md for more
99
 * information.
100
 */
101
#define SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER "SDL.video.wayland.wl_display"
102
103
/**
104
 * System theme.
105
 *
106
 * \since This enum is available since SDL 3.2.0.
107
 */
108
typedef enum SDL_SystemTheme
109
{
110
    SDL_SYSTEM_THEME_UNKNOWN,   /**< Unknown system theme */
111
    SDL_SYSTEM_THEME_LIGHT,     /**< Light colored system theme */
112
    SDL_SYSTEM_THEME_DARK       /**< Dark colored system theme */
113
} SDL_SystemTheme;
114
115
/**
116
 * Internal display mode data.
117
 *
118
 * This lives as a field in SDL_DisplayMode, as opaque data.
119
 *
120
 * \since This struct is available since SDL 3.2.0.
121
 *
122
 * \sa SDL_DisplayMode
123
 */
124
typedef struct SDL_DisplayModeData SDL_DisplayModeData;
125
126
/**
127
 * The structure that defines a display mode.
128
 *
129
 * \since This struct is available since SDL 3.2.0.
130
 *
131
 * \sa SDL_GetFullscreenDisplayModes
132
 * \sa SDL_GetDesktopDisplayMode
133
 * \sa SDL_GetCurrentDisplayMode
134
 * \sa SDL_SetWindowFullscreenMode
135
 * \sa SDL_GetWindowFullscreenMode
136
 */
137
typedef struct SDL_DisplayMode
138
{
139
    SDL_DisplayID displayID;        /**< the display this mode is associated with */
140
    SDL_PixelFormat format;         /**< pixel format */
141
    int w;                          /**< width */
142
    int h;                          /**< height */
143
    float pixel_density;            /**< scale converting size to pixels (e.g. a 1920x1080 mode with 2.0 scale would have 3840x2160 pixels) */
144
    float refresh_rate;             /**< refresh rate (or 0.0f for unspecified) */
145
    int refresh_rate_numerator;     /**< precise refresh rate numerator (or 0 for unspecified) */
146
    int refresh_rate_denominator;   /**< precise refresh rate denominator */
147
148
    SDL_DisplayModeData *internal;  /**< Private */
149
150
} SDL_DisplayMode;
151
152
/**
153
 * Display orientation values; the way a display is rotated.
154
 *
155
 * \since This enum is available since SDL 3.2.0.
156
 */
157
typedef enum SDL_DisplayOrientation
158
{
159
    SDL_ORIENTATION_UNKNOWN,            /**< The display orientation can't be determined */
160
    SDL_ORIENTATION_LANDSCAPE,          /**< The display is in landscape mode, with the right side up, relative to portrait mode */
161
    SDL_ORIENTATION_LANDSCAPE_FLIPPED,  /**< The display is in landscape mode, with the left side up, relative to portrait mode */
162
    SDL_ORIENTATION_PORTRAIT,           /**< The display is in portrait mode */
163
    SDL_ORIENTATION_PORTRAIT_FLIPPED    /**< The display is in portrait mode, upside down */
164
} SDL_DisplayOrientation;
165
166
/**
167
 * The struct used as an opaque handle to a window.
168
 *
169
 * \since This struct is available since SDL 3.2.0.
170
 *
171
 * \sa SDL_CreateWindow
172
 */
173
typedef struct SDL_Window SDL_Window;
174
175
/**
176
 * The flags on a window.
177
 *
178
 * These cover a lot of true/false, or on/off, window state. Some of it is
179
 * immutable after being set through SDL_CreateWindow(), some of it can be
180
 * changed on existing windows by the app, and some of it might be altered by
181
 * the user or system outside of the app's control.
182
 *
183
 * \since This datatype is available since SDL 3.2.0.
184
 *
185
 * \sa SDL_GetWindowFlags
186
 */
187
typedef Uint64 SDL_WindowFlags;
188
189
#define SDL_WINDOW_FULLSCREEN           SDL_UINT64_C(0x0000000000000001)    /**< window is in fullscreen mode */
190
#define SDL_WINDOW_OPENGL               SDL_UINT64_C(0x0000000000000002)    /**< window usable with OpenGL context */
191
#define SDL_WINDOW_OCCLUDED             SDL_UINT64_C(0x0000000000000004)    /**< window is occluded */
192
#define SDL_WINDOW_HIDDEN               SDL_UINT64_C(0x0000000000000008)    /**< window is neither mapped onto the desktop nor shown in the taskbar/dock/window list; SDL_ShowWindow() is required for it to become visible */
193
#define SDL_WINDOW_BORDERLESS           SDL_UINT64_C(0x0000000000000010)    /**< no window decoration */
194
1
#define SDL_WINDOW_RESIZABLE            SDL_UINT64_C(0x0000000000000020)    /**< window can be resized */
195
80.5k
#define SDL_WINDOW_MINIMIZED            SDL_UINT64_C(0x0000000000000040)    /**< window is minimized */
196
#define SDL_WINDOW_MAXIMIZED            SDL_UINT64_C(0x0000000000000080)    /**< window is maximized */
197
#define SDL_WINDOW_MOUSE_GRABBED        SDL_UINT64_C(0x0000000000000100)    /**< window has grabbed mouse input */
198
#define SDL_WINDOW_INPUT_FOCUS          SDL_UINT64_C(0x0000000000000200)    /**< window has input focus */
199
#define SDL_WINDOW_MOUSE_FOCUS          SDL_UINT64_C(0x0000000000000400)    /**< window has mouse focus */
200
#define SDL_WINDOW_EXTERNAL             SDL_UINT64_C(0x0000000000000800)    /**< window not created by SDL */
201
#define SDL_WINDOW_MODAL                SDL_UINT64_C(0x0000000000001000)    /**< window is modal */
202
#define SDL_WINDOW_HIGH_PIXEL_DENSITY   SDL_UINT64_C(0x0000000000002000)    /**< window uses high pixel density back buffer if possible */
203
#define SDL_WINDOW_MOUSE_CAPTURE        SDL_UINT64_C(0x0000000000004000)    /**< window has mouse captured (unrelated to MOUSE_GRABBED) */
204
#define SDL_WINDOW_MOUSE_RELATIVE_MODE  SDL_UINT64_C(0x0000000000008000)    /**< window has relative mode enabled */
205
#define SDL_WINDOW_ALWAYS_ON_TOP        SDL_UINT64_C(0x0000000000010000)    /**< window should always be above others */
206
#define SDL_WINDOW_UTILITY              SDL_UINT64_C(0x0000000000020000)    /**< window should be treated as a utility window, not showing in the task bar and window list */
207
#define SDL_WINDOW_TOOLTIP              SDL_UINT64_C(0x0000000000040000)    /**< window should be treated as a tooltip and does not get mouse or keyboard focus, requires a parent window */
208
#define SDL_WINDOW_POPUP_MENU           SDL_UINT64_C(0x0000000000080000)    /**< window should be treated as a popup menu, requires a parent window */
209
#define SDL_WINDOW_KEYBOARD_GRABBED     SDL_UINT64_C(0x0000000000100000)    /**< window has grabbed keyboard input */
210
#define SDL_WINDOW_VULKAN               SDL_UINT64_C(0x0000000010000000)    /**< window usable for Vulkan surface */
211
#define SDL_WINDOW_METAL                SDL_UINT64_C(0x0000000020000000)    /**< window usable for Metal view */
212
#define SDL_WINDOW_TRANSPARENT          SDL_UINT64_C(0x0000000040000000)    /**< window with transparent buffer */
213
#define SDL_WINDOW_NOT_FOCUSABLE        SDL_UINT64_C(0x0000000080000000)    /**< window should not be focusable */
214
215
216
/**
217
 * A magic value used with SDL_WINDOWPOS_UNDEFINED.
218
 *
219
 * Generally this macro isn't used directly, but rather through
220
 * SDL_WINDOWPOS_UNDEFINED or SDL_WINDOWPOS_UNDEFINED_DISPLAY.
221
 *
222
 * \since This macro is available since SDL 3.2.0.
223
 */
224
#define SDL_WINDOWPOS_UNDEFINED_MASK    0x1FFF0000u
225
226
/**
227
 * Used to indicate that you don't care what the window position is.
228
 *
229
 * If you _really_ don't care, SDL_WINDOWPOS_UNDEFINED is the same, but always
230
 * uses the primary display instead of specifying one.
231
 *
232
 * \param X the SDL_DisplayID of the display to use.
233
 *
234
 * \since This macro is available since SDL 3.2.0.
235
 */
236
#define SDL_WINDOWPOS_UNDEFINED_DISPLAY(X)  (SDL_WINDOWPOS_UNDEFINED_MASK|(X))
237
238
/**
239
 * Used to indicate that you don't care what the window position/display is.
240
 *
241
 * This always uses the primary display.
242
 *
243
 * \since This macro is available since SDL 3.2.0.
244
 */
245
#define SDL_WINDOWPOS_UNDEFINED         SDL_WINDOWPOS_UNDEFINED_DISPLAY(0)
246
247
/**
248
 * A macro to test if the window position is marked as "undefined."
249
 *
250
 * \param X the window position value.
251
 *
252
 * \since This macro is available since SDL 3.2.0.
253
 */
254
#define SDL_WINDOWPOS_ISUNDEFINED(X)    (((X)&0xFFFF0000) == SDL_WINDOWPOS_UNDEFINED_MASK)
255
256
/**
257
 * A magic value used with SDL_WINDOWPOS_CENTERED.
258
 *
259
 * Generally this macro isn't used directly, but rather through
260
 * SDL_WINDOWPOS_CENTERED or SDL_WINDOWPOS_CENTERED_DISPLAY.
261
 *
262
 * \since This macro is available since SDL 3.2.0.
263
 */
264
#define SDL_WINDOWPOS_CENTERED_MASK    0x2FFF0000u
265
266
/**
267
 * Used to indicate that the window position should be centered.
268
 *
269
 * SDL_WINDOWPOS_CENTERED is the same, but always uses the primary display
270
 * instead of specifying one.
271
 *
272
 * \param X the SDL_DisplayID of the display to use.
273
 *
274
 * \since This macro is available since SDL 3.2.0.
275
 */
276
#define SDL_WINDOWPOS_CENTERED_DISPLAY(X)  (SDL_WINDOWPOS_CENTERED_MASK|(X))
277
278
/**
279
 * Used to indicate that the window position should be centered.
280
 *
281
 * This always uses the primary display.
282
 *
283
 * \since This macro is available since SDL 3.2.0.
284
 */
285
#define SDL_WINDOWPOS_CENTERED         SDL_WINDOWPOS_CENTERED_DISPLAY(0)
286
287
/**
288
 * A macro to test if the window position is marked as "centered."
289
 *
290
 * \param X the window position value.
291
 *
292
 * \since This macro is available since SDL 3.2.0.
293
 */
294
#define SDL_WINDOWPOS_ISCENTERED(X)    \
295
            (((X)&0xFFFF0000) == SDL_WINDOWPOS_CENTERED_MASK)
296
297
298
/**
299
 * Window flash operation.
300
 *
301
 * \since This enum is available since SDL 3.2.0.
302
 */
303
typedef enum SDL_FlashOperation
304
{
305
    SDL_FLASH_CANCEL,                   /**< Cancel any window flash state */
306
    SDL_FLASH_BRIEFLY,                  /**< Flash the window briefly to get attention */
307
    SDL_FLASH_UNTIL_FOCUSED             /**< Flash the window until it gets focus */
308
} SDL_FlashOperation;
309
310
/**
311
 * An opaque handle to an OpenGL context.
312
 *
313
 * \since This datatype is available since SDL 3.2.0.
314
 *
315
 * \sa SDL_GL_CreateContext
316
 */
317
typedef struct SDL_GLContextState *SDL_GLContext;
318
319
/**
320
 * Opaque type for an EGL display.
321
 *
322
 * \since This datatype is available since SDL 3.2.0.
323
 */
324
typedef void *SDL_EGLDisplay;
325
326
/**
327
 * Opaque type for an EGL config.
328
 *
329
 * \since This datatype is available since SDL 3.2.0.
330
 */
331
typedef void *SDL_EGLConfig;
332
333
/**
334
 * Opaque type for an EGL surface.
335
 *
336
 * \since This datatype is available since SDL 3.2.0.
337
 */
338
typedef void *SDL_EGLSurface;
339
340
/**
341
 * An EGL attribute, used when creating an EGL context.
342
 *
343
 * \since This datatype is available since SDL 3.2.0.
344
 */
345
typedef intptr_t SDL_EGLAttrib;
346
347
/**
348
 * An EGL integer attribute, used when creating an EGL surface.
349
 *
350
 * \since This datatype is available since SDL 3.2.0.
351
 */
352
typedef int SDL_EGLint;
353
354
/**
355
 * EGL platform attribute initialization callback.
356
 *
357
 * This is called when SDL is attempting to create an EGL context, to let the
358
 * app add extra attributes to its eglGetPlatformDisplay() call.
359
 *
360
 * The callback should return a pointer to an EGL attribute array terminated
361
 * with `EGL_NONE`. If this function returns NULL, the SDL_CreateWindow
362
 * process will fail gracefully.
363
 *
364
 * The returned pointer should be allocated with SDL_malloc() and will be
365
 * passed to SDL_free().
366
 *
367
 * The arrays returned by each callback will be appended to the existing
368
 * attribute arrays defined by SDL.
369
 *
370
 * \param userdata an app-controlled pointer that is passed to the callback.
371
 * \returns a newly-allocated array of attributes, terminated with `EGL_NONE`.
372
 *
373
 * \since This datatype is available since SDL 3.2.0.
374
 *
375
 * \sa SDL_EGL_SetAttributeCallbacks
376
 */
377
typedef SDL_EGLAttrib *(SDLCALL *SDL_EGLAttribArrayCallback)(void *userdata);
378
379
/**
380
 * EGL surface/context attribute initialization callback types.
381
 *
382
 * This is called when SDL is attempting to create an EGL surface, to let the
383
 * app add extra attributes to its eglCreateWindowSurface() or
384
 * eglCreateContext calls.
385
 *
386
 * For convenience, the EGLDisplay and EGLConfig to use are provided to the
387
 * callback.
388
 *
389
 * The callback should return a pointer to an EGL attribute array terminated
390
 * with `EGL_NONE`. If this function returns NULL, the SDL_CreateWindow
391
 * process will fail gracefully.
392
 *
393
 * The returned pointer should be allocated with SDL_malloc() and will be
394
 * passed to SDL_free().
395
 *
396
 * The arrays returned by each callback will be appended to the existing
397
 * attribute arrays defined by SDL.
398
 *
399
 * \param userdata an app-controlled pointer that is passed to the callback.
400
 * \param display the EGL display to be used.
401
 * \param config the EGL config to be used.
402
 * \returns a newly-allocated array of attributes, terminated with `EGL_NONE`.
403
 *
404
 * \since This datatype is available since SDL 3.2.0.
405
 *
406
 * \sa SDL_EGL_SetAttributeCallbacks
407
 */
408
typedef SDL_EGLint *(SDLCALL *SDL_EGLIntArrayCallback)(void *userdata, SDL_EGLDisplay display, SDL_EGLConfig config);
409
410
/**
411
 * An enumeration of OpenGL configuration attributes.
412
 *
413
 * While you can set most OpenGL attributes normally, the attributes listed
414
 * above must be known before SDL creates the window that will be used with
415
 * the OpenGL context. These attributes are set and read with
416
 * SDL_GL_SetAttribute() and SDL_GL_GetAttribute().
417
 *
418
 * In some cases, these attributes are minimum requests; the GL does not
419
 * promise to give you exactly what you asked for. It's possible to ask for a
420
 * 16-bit depth buffer and get a 24-bit one instead, for example, or to ask
421
 * for no stencil buffer and still have one available. Context creation should
422
 * fail if the GL can't provide your requested attributes at a minimum, but
423
 * you should check to see exactly what you got.
424
 *
425
 * \since This enum is available since SDL 3.2.0.
426
 */
427
typedef enum SDL_GLAttr
428
{
429
    SDL_GL_RED_SIZE,                    /**< the minimum number of bits for the red channel of the color buffer; defaults to 8. */
430
    SDL_GL_GREEN_SIZE,                  /**< the minimum number of bits for the green channel of the color buffer; defaults to 8. */
431
    SDL_GL_BLUE_SIZE,                   /**< the minimum number of bits for the blue channel of the color buffer; defaults to 8. */
432
    SDL_GL_ALPHA_SIZE,                  /**< the minimum number of bits for the alpha channel of the color buffer; defaults to 8. */
433
    SDL_GL_BUFFER_SIZE,                 /**< the minimum number of bits for frame buffer size; defaults to 0. */
434
    SDL_GL_DOUBLEBUFFER,                /**< whether the output is single or double buffered; defaults to double buffering on. */
435
    SDL_GL_DEPTH_SIZE,                  /**< the minimum number of bits in the depth buffer; defaults to 16. */
436
    SDL_GL_STENCIL_SIZE,                /**< the minimum number of bits in the stencil buffer; defaults to 0. */
437
    SDL_GL_ACCUM_RED_SIZE,              /**< the minimum number of bits for the red channel of the accumulation buffer; defaults to 0. */
438
    SDL_GL_ACCUM_GREEN_SIZE,            /**< the minimum number of bits for the green channel of the accumulation buffer; defaults to 0. */
439
    SDL_GL_ACCUM_BLUE_SIZE,             /**< the minimum number of bits for the blue channel of the accumulation buffer; defaults to 0. */
440
    SDL_GL_ACCUM_ALPHA_SIZE,            /**< the minimum number of bits for the alpha channel of the accumulation buffer; defaults to 0. */
441
    SDL_GL_STEREO,                      /**< whether the output is stereo 3D; defaults to off. */
442
    SDL_GL_MULTISAMPLEBUFFERS,          /**< the number of buffers used for multisample anti-aliasing; defaults to 0. */
443
    SDL_GL_MULTISAMPLESAMPLES,          /**< the number of samples used around the current pixel used for multisample anti-aliasing. */
444
    SDL_GL_ACCELERATED_VISUAL,          /**< set to 1 to require hardware acceleration, set to 0 to force software rendering; defaults to allow either. */
445
    SDL_GL_RETAINED_BACKING,            /**< not used (deprecated). */
446
    SDL_GL_CONTEXT_MAJOR_VERSION,       /**< OpenGL context major version. */
447
    SDL_GL_CONTEXT_MINOR_VERSION,       /**< OpenGL context minor version. */
448
    SDL_GL_CONTEXT_FLAGS,               /**< some combination of 0 or more of elements of the SDL_GLContextFlag enumeration; defaults to 0. */
449
    SDL_GL_CONTEXT_PROFILE_MASK,        /**< type of GL context (Core, Compatibility, ES). See SDL_GLProfile; default value depends on platform. */
450
    SDL_GL_SHARE_WITH_CURRENT_CONTEXT,  /**< OpenGL context sharing; defaults to 0. */
451
    SDL_GL_FRAMEBUFFER_SRGB_CAPABLE,    /**< requests sRGB capable visual; defaults to 0. */
452
    SDL_GL_CONTEXT_RELEASE_BEHAVIOR,    /**< sets context the release behavior. See SDL_GLContextReleaseFlag; defaults to FLUSH. */
453
    SDL_GL_CONTEXT_RESET_NOTIFICATION,  /**< set context reset notification. See SDL_GLContextResetNotification; defaults to NO_NOTIFICATION. */
454
    SDL_GL_CONTEXT_NO_ERROR,
455
    SDL_GL_FLOATBUFFERS,
456
    SDL_GL_EGL_PLATFORM
457
} SDL_GLAttr;
458
459
/**
460
 * Possible values to be set for the SDL_GL_CONTEXT_PROFILE_MASK attribute.
461
 *
462
 * \since This datatype is available since SDL 3.2.0.
463
 */
464
typedef Uint32 SDL_GLProfile;
465
466
#define SDL_GL_CONTEXT_PROFILE_CORE           0x0001  /**< OpenGL Core Profile context */
467
#define SDL_GL_CONTEXT_PROFILE_COMPATIBILITY  0x0002  /**< OpenGL Compatibility Profile context */
468
#define SDL_GL_CONTEXT_PROFILE_ES             0x0004  /**< GLX_CONTEXT_ES2_PROFILE_BIT_EXT */
469
470
471
/**
472
 * Possible flags to be set for the SDL_GL_CONTEXT_FLAGS attribute.
473
 *
474
 * \since This datatype is available since SDL 3.2.0.
475
 */
476
typedef Uint32 SDL_GLContextFlag;
477
478
#define SDL_GL_CONTEXT_DEBUG_FLAG              0x0001
479
#define SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG 0x0002
480
#define SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG      0x0004
481
#define SDL_GL_CONTEXT_RESET_ISOLATION_FLAG    0x0008
482
483
484
/**
485
 * Possible values to be set for the SDL_GL_CONTEXT_RELEASE_BEHAVIOR
486
 * attribute.
487
 *
488
 * \since This datatype is available since SDL 3.2.0.
489
 */
490
typedef Uint32 SDL_GLContextReleaseFlag;
491
492
#define SDL_GL_CONTEXT_RELEASE_BEHAVIOR_NONE   0x0000
493
#define SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH  0x0001
494
495
496
/**
497
 * Possible values to be set SDL_GL_CONTEXT_RESET_NOTIFICATION attribute.
498
 *
499
 * \since This datatype is available since SDL 3.2.0.
500
 */
501
typedef Uint32 SDL_GLContextResetNotification;
502
503
#define SDL_GL_CONTEXT_RESET_NO_NOTIFICATION  0x0000
504
#define SDL_GL_CONTEXT_RESET_LOSE_CONTEXT     0x0001
505
506
507
/* Function prototypes */
508
509
/**
510
 * Get the number of video drivers compiled into SDL.
511
 *
512
 * \returns the number of built in video drivers.
513
 *
514
 * \threadsafety This function should only be called on the main thread.
515
 *
516
 * \since This function is available since SDL 3.2.0.
517
 *
518
 * \sa SDL_GetVideoDriver
519
 */
520
extern SDL_DECLSPEC int SDLCALL SDL_GetNumVideoDrivers(void);
521
522
/**
523
 * Get the name of a built in video driver.
524
 *
525
 * The video drivers are presented in the order in which they are normally
526
 * checked during initialization.
527
 *
528
 * The names of drivers are all simple, low-ASCII identifiers, like "cocoa",
529
 * "x11" or "windows". These never have Unicode characters, and are not meant
530
 * to be proper names.
531
 *
532
 * \param index the index of a video driver.
533
 * \returns the name of the video driver with the given **index**.
534
 *
535
 * \threadsafety This function should only be called on the main thread.
536
 *
537
 * \since This function is available since SDL 3.2.0.
538
 *
539
 * \sa SDL_GetNumVideoDrivers
540
 */
541
extern SDL_DECLSPEC const char * SDLCALL SDL_GetVideoDriver(int index);
542
543
/**
544
 * Get the name of the currently initialized video driver.
545
 *
546
 * The names of drivers are all simple, low-ASCII identifiers, like "cocoa",
547
 * "x11" or "windows". These never have Unicode characters, and are not meant
548
 * to be proper names.
549
 *
550
 * \returns the name of the current video driver or NULL if no driver has been
551
 *          initialized.
552
 *
553
 * \threadsafety This function should only be called on the main thread.
554
 *
555
 * \since This function is available since SDL 3.2.0.
556
 *
557
 * \sa SDL_GetNumVideoDrivers
558
 * \sa SDL_GetVideoDriver
559
 */
560
extern SDL_DECLSPEC const char * SDLCALL SDL_GetCurrentVideoDriver(void);
561
562
/**
563
 * Get the current system theme.
564
 *
565
 * \returns the current system theme, light, dark, or unknown.
566
 *
567
 * \threadsafety This function should only be called on the main thread.
568
 *
569
 * \since This function is available since SDL 3.2.0.
570
 */
571
extern SDL_DECLSPEC SDL_SystemTheme SDLCALL SDL_GetSystemTheme(void);
572
573
/**
574
 * Get a list of currently connected displays.
575
 *
576
 * \param count a pointer filled in with the number of displays returned, may
577
 *              be NULL.
578
 * \returns a 0 terminated array of display instance IDs or NULL on failure;
579
 *          call SDL_GetError() for more information. This should be freed
580
 *          with SDL_free() when it is no longer needed.
581
 *
582
 * \threadsafety This function should only be called on the main thread.
583
 *
584
 * \since This function is available since SDL 3.2.0.
585
 */
586
extern SDL_DECLSPEC SDL_DisplayID * SDLCALL SDL_GetDisplays(int *count);
587
588
/**
589
 * Return the primary display.
590
 *
591
 * \returns the instance ID of the primary display on success or 0 on failure;
592
 *          call SDL_GetError() for more information.
593
 *
594
 * \threadsafety This function should only be called on the main thread.
595
 *
596
 * \since This function is available since SDL 3.2.0.
597
 *
598
 * \sa SDL_GetDisplays
599
 */
600
extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void);
601
602
/**
603
 * Get the properties associated with a display.
604
 *
605
 * The following read-only properties are provided by SDL:
606
 *
607
 * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR
608
 *   headroom above the SDR white point. This is for informational and
609
 *   diagnostic purposes only, as not all platforms provide this information
610
 *   at the display level.
611
 *
612
 * On KMS/DRM:
613
 *
614
 * - `SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER`: the "panel
615
 *   orientation" property for the display in degrees of clockwise rotation.
616
 *   Note that this is provided only as a hint, and the application is
617
 *   responsible for any coordinate transformations needed to conform to the
618
 *   requested display orientation.
619
 *
620
 * \param displayID the instance ID of the display to query.
621
 * \returns a valid property ID on success or 0 on failure; call
622
 *          SDL_GetError() for more information.
623
 *
624
 * \threadsafety This function should only be called on the main thread.
625
 *
626
 * \since This function is available since SDL 3.2.0.
627
 */
628
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_DisplayID displayID);
629
630
#define SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN             "SDL.display.HDR_enabled"
631
#define SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER "SDL.display.KMSDRM.panel_orientation"
632
633
/**
634
 * Get the name of a display in UTF-8 encoding.
635
 *
636
 * \param displayID the instance ID of the display to query.
637
 * \returns the name of a display or NULL on failure; call SDL_GetError() for
638
 *          more information.
639
 *
640
 * \threadsafety This function should only be called on the main thread.
641
 *
642
 * \since This function is available since SDL 3.2.0.
643
 *
644
 * \sa SDL_GetDisplays
645
 */
646
extern SDL_DECLSPEC const char * SDLCALL SDL_GetDisplayName(SDL_DisplayID displayID);
647
648
/**
649
 * Get the desktop area represented by a display.
650
 *
651
 * The primary display is often located at (0,0), but may be placed at a
652
 * different location depending on monitor layout.
653
 *
654
 * \param displayID the instance ID of the display to query.
655
 * \param rect the SDL_Rect structure filled in with the display bounds.
656
 * \returns true on success or false on failure; call SDL_GetError() for more
657
 *          information.
658
 *
659
 * \threadsafety This function should only be called on the main thread.
660
 *
661
 * \since This function is available since SDL 3.2.0.
662
 *
663
 * \sa SDL_GetDisplayUsableBounds
664
 * \sa SDL_GetDisplays
665
 */
666
extern SDL_DECLSPEC bool SDLCALL SDL_GetDisplayBounds(SDL_DisplayID displayID, SDL_Rect *rect);
667
668
/**
669
 * Get the usable desktop area represented by a display, in screen
670
 * coordinates.
671
 *
672
 * This is the same area as SDL_GetDisplayBounds() reports, but with portions
673
 * reserved by the system removed. For example, on Apple's macOS, this
674
 * subtracts the area occupied by the menu bar and dock.
675
 *
676
 * Setting a window to be fullscreen generally bypasses these unusable areas,
677
 * so these are good guidelines for the maximum space available to a
678
 * non-fullscreen window.
679
 *
680
 * \param displayID the instance ID of the display to query.
681
 * \param rect the SDL_Rect structure filled in with the display bounds.
682
 * \returns true on success or false on failure; call SDL_GetError() for more
683
 *          information.
684
 *
685
 * \threadsafety This function should only be called on the main thread.
686
 *
687
 * \since This function is available since SDL 3.2.0.
688
 *
689
 * \sa SDL_GetDisplayBounds
690
 * \sa SDL_GetDisplays
691
 */
692
extern SDL_DECLSPEC bool SDLCALL SDL_GetDisplayUsableBounds(SDL_DisplayID displayID, SDL_Rect *rect);
693
694
/**
695
 * Get the orientation of a display when it is unrotated.
696
 *
697
 * \param displayID the instance ID of the display to query.
698
 * \returns the SDL_DisplayOrientation enum value of the display, or
699
 *          `SDL_ORIENTATION_UNKNOWN` if it isn't available.
700
 *
701
 * \threadsafety This function should only be called on the main thread.
702
 *
703
 * \since This function is available since SDL 3.2.0.
704
 *
705
 * \sa SDL_GetDisplays
706
 */
707
extern SDL_DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetNaturalDisplayOrientation(SDL_DisplayID displayID);
708
709
/**
710
 * Get the orientation of a display.
711
 *
712
 * \param displayID the instance ID of the display to query.
713
 * \returns the SDL_DisplayOrientation enum value of the display, or
714
 *          `SDL_ORIENTATION_UNKNOWN` if it isn't available.
715
 *
716
 * \threadsafety This function should only be called on the main thread.
717
 *
718
 * \since This function is available since SDL 3.2.0.
719
 *
720
 * \sa SDL_GetDisplays
721
 */
722
extern SDL_DECLSPEC SDL_DisplayOrientation SDLCALL SDL_GetCurrentDisplayOrientation(SDL_DisplayID displayID);
723
724
/**
725
 * Get the content scale of a display.
726
 *
727
 * The content scale is the expected scale for content based on the DPI
728
 * settings of the display. For example, a 4K display might have a 2.0 (200%)
729
 * display scale, which means that the user expects UI elements to be twice as
730
 * big on this display, to aid in readability.
731
 *
732
 * After window creation, SDL_GetWindowDisplayScale() should be used to query
733
 * the content scale factor for individual windows instead of querying the
734
 * display for a window and calling this function, as the per-window content
735
 * scale factor may differ from the base value of the display it is on,
736
 * particularly on high-DPI and/or multi-monitor desktop configurations.
737
 *
738
 * \param displayID the instance ID of the display to query.
739
 * \returns the content scale of the display, or 0.0f on failure; call
740
 *          SDL_GetError() for more information.
741
 *
742
 * \threadsafety This function should only be called on the main thread.
743
 *
744
 * \since This function is available since SDL 3.2.0.
745
 *
746
 * \sa SDL_GetWindowDisplayScale
747
 * \sa SDL_GetDisplays
748
 */
749
extern SDL_DECLSPEC float SDLCALL SDL_GetDisplayContentScale(SDL_DisplayID displayID);
750
751
/**
752
 * Get a list of fullscreen display modes available on a display.
753
 *
754
 * The display modes are sorted in this priority:
755
 *
756
 * - w -> largest to smallest
757
 * - h -> largest to smallest
758
 * - bits per pixel -> more colors to fewer colors
759
 * - packed pixel layout -> largest to smallest
760
 * - refresh rate -> highest to lowest
761
 * - pixel density -> lowest to highest
762
 *
763
 * \param displayID the instance ID of the display to query.
764
 * \param count a pointer filled in with the number of display modes returned,
765
 *              may be NULL.
766
 * \returns a NULL terminated array of display mode pointers or NULL on
767
 *          failure; call SDL_GetError() for more information. This is a
768
 *          single allocation that should be freed with SDL_free() when it is
769
 *          no longer needed.
770
 *
771
 * \threadsafety This function should only be called on the main thread.
772
 *
773
 * \since This function is available since SDL 3.2.0.
774
 *
775
 * \sa SDL_GetDisplays
776
 */
777
extern SDL_DECLSPEC SDL_DisplayMode ** SDLCALL SDL_GetFullscreenDisplayModes(SDL_DisplayID displayID, int *count);
778
779
/**
780
 * Get the closest match to the requested display mode.
781
 *
782
 * The available display modes are scanned and `closest` is filled in with the
783
 * closest mode matching the requested mode and returned. The mode format and
784
 * refresh rate default to the desktop mode if they are set to 0. The modes
785
 * are scanned with size being first priority, format being second priority,
786
 * and finally checking the refresh rate. If all the available modes are too
787
 * small, then false is returned.
788
 *
789
 * \param displayID the instance ID of the display to query.
790
 * \param w the width in pixels of the desired display mode.
791
 * \param h the height in pixels of the desired display mode.
792
 * \param refresh_rate the refresh rate of the desired display mode, or 0.0f
793
 *                     for the desktop refresh rate.
794
 * \param include_high_density_modes boolean to include high density modes in
795
 *                                   the search.
796
 * \param closest a pointer filled in with the closest display mode equal to
797
 *                or larger than the desired mode.
798
 * \returns true on success or false on failure; call SDL_GetError() for more
799
 *          information.
800
 *
801
 * \threadsafety This function should only be called on the main thread.
802
 *
803
 * \since This function is available since SDL 3.2.0.
804
 *
805
 * \sa SDL_GetDisplays
806
 * \sa SDL_GetFullscreenDisplayModes
807
 */
808
extern SDL_DECLSPEC bool SDLCALL SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, float refresh_rate, bool include_high_density_modes, SDL_DisplayMode *closest);
809
810
/**
811
 * Get information about the desktop's display mode.
812
 *
813
 * There's a difference between this function and SDL_GetCurrentDisplayMode()
814
 * when SDL runs fullscreen and has changed the resolution. In that case this
815
 * function will return the previous native display mode, and not the current
816
 * display mode.
817
 *
818
 * \param displayID the instance ID of the display to query.
819
 * \returns a pointer to the desktop display mode or NULL on failure; call
820
 *          SDL_GetError() for more information.
821
 *
822
 * \threadsafety This function should only be called on the main thread.
823
 *
824
 * \since This function is available since SDL 3.2.0.
825
 *
826
 * \sa SDL_GetCurrentDisplayMode
827
 * \sa SDL_GetDisplays
828
 */
829
extern SDL_DECLSPEC const SDL_DisplayMode * SDLCALL SDL_GetDesktopDisplayMode(SDL_DisplayID displayID);
830
831
/**
832
 * Get information about the current display mode.
833
 *
834
 * There's a difference between this function and SDL_GetDesktopDisplayMode()
835
 * when SDL runs fullscreen and has changed the resolution. In that case this
836
 * function will return the current display mode, and not the previous native
837
 * display mode.
838
 *
839
 * \param displayID the instance ID of the display to query.
840
 * \returns a pointer to the desktop display mode or NULL on failure; call
841
 *          SDL_GetError() for more information.
842
 *
843
 * \threadsafety This function should only be called on the main thread.
844
 *
845
 * \since This function is available since SDL 3.2.0.
846
 *
847
 * \sa SDL_GetDesktopDisplayMode
848
 * \sa SDL_GetDisplays
849
 */
850
extern SDL_DECLSPEC const SDL_DisplayMode * SDLCALL SDL_GetCurrentDisplayMode(SDL_DisplayID displayID);
851
852
/**
853
 * Get the display containing a point.
854
 *
855
 * \param point the point to query.
856
 * \returns the instance ID of the display containing the point or 0 on
857
 *          failure; call SDL_GetError() for more information.
858
 *
859
 * \threadsafety This function should only be called on the main thread.
860
 *
861
 * \since This function is available since SDL 3.2.0.
862
 *
863
 * \sa SDL_GetDisplayBounds
864
 * \sa SDL_GetDisplays
865
 */
866
extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetDisplayForPoint(const SDL_Point *point);
867
868
/**
869
 * Get the display primarily containing a rect.
870
 *
871
 * \param rect the rect to query.
872
 * \returns the instance ID of the display entirely containing the rect or
873
 *          closest to the center of the rect on success or 0 on failure; call
874
 *          SDL_GetError() for more information.
875
 *
876
 * \threadsafety This function should only be called on the main thread.
877
 *
878
 * \since This function is available since SDL 3.2.0.
879
 *
880
 * \sa SDL_GetDisplayBounds
881
 * \sa SDL_GetDisplays
882
 */
883
extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetDisplayForRect(const SDL_Rect *rect);
884
885
/**
886
 * Get the display associated with a window.
887
 *
888
 * \param window the window to query.
889
 * \returns the instance ID of the display containing the center of the window
890
 *          on success or 0 on failure; call SDL_GetError() for more
891
 *          information.
892
 *
893
 * \threadsafety This function should only be called on the main thread.
894
 *
895
 * \since This function is available since SDL 3.2.0.
896
 *
897
 * \sa SDL_GetDisplayBounds
898
 * \sa SDL_GetDisplays
899
 */
900
extern SDL_DECLSPEC SDL_DisplayID SDLCALL SDL_GetDisplayForWindow(SDL_Window *window);
901
902
/**
903
 * Get the pixel density of a window.
904
 *
905
 * This is a ratio of pixel size to window size. For example, if the window is
906
 * 1920x1080 and it has a high density back buffer of 3840x2160 pixels, it
907
 * would have a pixel density of 2.0.
908
 *
909
 * \param window the window to query.
910
 * \returns the pixel density or 0.0f on failure; call SDL_GetError() for more
911
 *          information.
912
 *
913
 * \threadsafety This function should only be called on the main thread.
914
 *
915
 * \since This function is available since SDL 3.2.0.
916
 *
917
 * \sa SDL_GetWindowDisplayScale
918
 */
919
extern SDL_DECLSPEC float SDLCALL SDL_GetWindowPixelDensity(SDL_Window *window);
920
921
/**
922
 * Get the content display scale relative to a window's pixel size.
923
 *
924
 * This is a combination of the window pixel density and the display content
925
 * scale, and is the expected scale for displaying content in this window. For
926
 * example, if a 3840x2160 window had a display scale of 2.0, the user expects
927
 * the content to take twice as many pixels and be the same physical size as
928
 * if it were being displayed in a 1920x1080 window with a display scale of
929
 * 1.0.
930
 *
931
 * Conceptually this value corresponds to the scale display setting, and is
932
 * updated when that setting is changed, or the window moves to a display with
933
 * a different scale setting.
934
 *
935
 * \param window the window to query.
936
 * \returns the display scale, or 0.0f on failure; call SDL_GetError() for
937
 *          more information.
938
 *
939
 * \threadsafety This function should only be called on the main thread.
940
 *
941
 * \since This function is available since SDL 3.2.0.
942
 */
943
extern SDL_DECLSPEC float SDLCALL SDL_GetWindowDisplayScale(SDL_Window *window);
944
945
/**
946
 * Set the display mode to use when a window is visible and fullscreen.
947
 *
948
 * This only affects the display mode used when the window is fullscreen. To
949
 * change the window size when the window is not fullscreen, use
950
 * SDL_SetWindowSize().
951
 *
952
 * If the window is currently in the fullscreen state, this request is
953
 * asynchronous on some windowing systems and the new mode dimensions may not
954
 * be applied immediately upon the return of this function. If an immediate
955
 * change is required, call SDL_SyncWindow() to block until the changes have
956
 * taken effect.
957
 *
958
 * When the new mode takes effect, an SDL_EVENT_WINDOW_RESIZED and/or an
959
 * SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED event will be emitted with the new mode
960
 * dimensions.
961
 *
962
 * \param window the window to affect.
963
 * \param mode a pointer to the display mode to use, which can be NULL for
964
 *             borderless fullscreen desktop mode, or one of the fullscreen
965
 *             modes returned by SDL_GetFullscreenDisplayModes() to set an
966
 *             exclusive fullscreen mode.
967
 * \returns true on success or false on failure; call SDL_GetError() for more
968
 *          information.
969
 *
970
 * \threadsafety This function should only be called on the main thread.
971
 *
972
 * \since This function is available since SDL 3.2.0.
973
 *
974
 * \sa SDL_GetWindowFullscreenMode
975
 * \sa SDL_SetWindowFullscreen
976
 * \sa SDL_SyncWindow
977
 */
978
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFullscreenMode(SDL_Window *window, const SDL_DisplayMode *mode);
979
980
/**
981
 * Query the display mode to use when a window is visible at fullscreen.
982
 *
983
 * \param window the window to query.
984
 * \returns a pointer to the exclusive fullscreen mode to use or NULL for
985
 *          borderless fullscreen desktop mode.
986
 *
987
 * \threadsafety This function should only be called on the main thread.
988
 *
989
 * \since This function is available since SDL 3.2.0.
990
 *
991
 * \sa SDL_SetWindowFullscreenMode
992
 * \sa SDL_SetWindowFullscreen
993
 */
994
extern SDL_DECLSPEC const SDL_DisplayMode * SDLCALL SDL_GetWindowFullscreenMode(SDL_Window *window);
995
996
/**
997
 * Get the raw ICC profile data for the screen the window is currently on.
998
 *
999
 * \param window the window to query.
1000
 * \param size the size of the ICC profile.
1001
 * \returns the raw ICC profile data on success or NULL on failure; call
1002
 *          SDL_GetError() for more information. This should be freed with
1003
 *          SDL_free() when it is no longer needed.
1004
 *
1005
 * \threadsafety This function should only be called on the main thread.
1006
 *
1007
 * \since This function is available since SDL 3.2.0.
1008
 */
1009
extern SDL_DECLSPEC void * SDLCALL SDL_GetWindowICCProfile(SDL_Window *window, size_t *size);
1010
1011
/**
1012
 * Get the pixel format associated with the window.
1013
 *
1014
 * \param window the window to query.
1015
 * \returns the pixel format of the window on success or
1016
 *          SDL_PIXELFORMAT_UNKNOWN on failure; call SDL_GetError() for more
1017
 *          information.
1018
 *
1019
 * \threadsafety This function should only be called on the main thread.
1020
 *
1021
 * \since This function is available since SDL 3.2.0.
1022
 */
1023
extern SDL_DECLSPEC SDL_PixelFormat SDLCALL SDL_GetWindowPixelFormat(SDL_Window *window);
1024
1025
/**
1026
 * Get a list of valid windows.
1027
 *
1028
 * \param count a pointer filled in with the number of windows returned, may
1029
 *              be NULL.
1030
 * \returns a NULL terminated array of SDL_Window pointers or NULL on failure;
1031
 *          call SDL_GetError() for more information. This is a single
1032
 *          allocation that should be freed with SDL_free() when it is no
1033
 *          longer needed.
1034
 *
1035
 * \threadsafety This function should only be called on the main thread.
1036
 *
1037
 * \since This function is available since SDL 3.2.0.
1038
 */
1039
extern SDL_DECLSPEC SDL_Window ** SDLCALL SDL_GetWindows(int *count);
1040
1041
/**
1042
 * Create a window with the specified dimensions and flags.
1043
 *
1044
 * The window size is a request and may be different than expected based on
1045
 * the desktop layout and window manager policies. Your application should be
1046
 * prepared to handle a window of any size.
1047
 *
1048
 * `flags` may be any of the following OR'd together:
1049
 *
1050
 * - `SDL_WINDOW_FULLSCREEN`: fullscreen window at desktop resolution
1051
 * - `SDL_WINDOW_OPENGL`: window usable with an OpenGL context
1052
 * - `SDL_WINDOW_OCCLUDED`: window partially or completely obscured by another
1053
 *   window
1054
 * - `SDL_WINDOW_HIDDEN`: window is not visible
1055
 * - `SDL_WINDOW_BORDERLESS`: no window decoration
1056
 * - `SDL_WINDOW_RESIZABLE`: window can be resized
1057
 * - `SDL_WINDOW_MINIMIZED`: window is minimized
1058
 * - `SDL_WINDOW_MAXIMIZED`: window is maximized
1059
 * - `SDL_WINDOW_MOUSE_GRABBED`: window has grabbed mouse focus
1060
 * - `SDL_WINDOW_INPUT_FOCUS`: window has input focus
1061
 * - `SDL_WINDOW_MOUSE_FOCUS`: window has mouse focus
1062
 * - `SDL_WINDOW_EXTERNAL`: window not created by SDL
1063
 * - `SDL_WINDOW_MODAL`: window is modal
1064
 * - `SDL_WINDOW_HIGH_PIXEL_DENSITY`: window uses high pixel density back
1065
 *   buffer if possible
1066
 * - `SDL_WINDOW_MOUSE_CAPTURE`: window has mouse captured (unrelated to
1067
 *   MOUSE_GRABBED)
1068
 * - `SDL_WINDOW_ALWAYS_ON_TOP`: window should always be above others
1069
 * - `SDL_WINDOW_UTILITY`: window should be treated as a utility window, not
1070
 *   showing in the task bar and window list
1071
 * - `SDL_WINDOW_TOOLTIP`: window should be treated as a tooltip and does not
1072
 *   get mouse or keyboard focus, requires a parent window
1073
 * - `SDL_WINDOW_POPUP_MENU`: window should be treated as a popup menu,
1074
 *   requires a parent window
1075
 * - `SDL_WINDOW_KEYBOARD_GRABBED`: window has grabbed keyboard input
1076
 * - `SDL_WINDOW_VULKAN`: window usable with a Vulkan instance
1077
 * - `SDL_WINDOW_METAL`: window usable with a Metal instance
1078
 * - `SDL_WINDOW_TRANSPARENT`: window with transparent buffer
1079
 * - `SDL_WINDOW_NOT_FOCUSABLE`: window should not be focusable
1080
 *
1081
 * The SDL_Window is implicitly shown if SDL_WINDOW_HIDDEN is not set.
1082
 *
1083
 * On Apple's macOS, you **must** set the NSHighResolutionCapable Info.plist
1084
 * property to YES, otherwise you will not receive a High-DPI OpenGL canvas.
1085
 *
1086
 * The window pixel size may differ from its window coordinate size if the
1087
 * window is on a high pixel density display. Use SDL_GetWindowSize() to query
1088
 * the client area's size in window coordinates, and
1089
 * SDL_GetWindowSizeInPixels() or SDL_GetRenderOutputSize() to query the
1090
 * drawable size in pixels. Note that the drawable size can vary after the
1091
 * window is created and should be queried again if you get an
1092
 * SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED event.
1093
 *
1094
 * If the window is created with any of the SDL_WINDOW_OPENGL or
1095
 * SDL_WINDOW_VULKAN flags, then the corresponding LoadLibrary function
1096
 * (SDL_GL_LoadLibrary or SDL_Vulkan_LoadLibrary) is called and the
1097
 * corresponding UnloadLibrary function is called by SDL_DestroyWindow().
1098
 *
1099
 * If SDL_WINDOW_VULKAN is specified and there isn't a working Vulkan driver,
1100
 * SDL_CreateWindow() will fail, because SDL_Vulkan_LoadLibrary() will fail.
1101
 *
1102
 * If SDL_WINDOW_METAL is specified on an OS that does not support Metal,
1103
 * SDL_CreateWindow() will fail.
1104
 *
1105
 * If you intend to use this window with an SDL_Renderer, you should use
1106
 * SDL_CreateWindowAndRenderer() instead of this function, to avoid window
1107
 * flicker.
1108
 *
1109
 * On non-Apple devices, SDL requires you to either not link to the Vulkan
1110
 * loader or link to a dynamic library version. This limitation may be removed
1111
 * in a future version of SDL.
1112
 *
1113
 * \param title the title of the window, in UTF-8 encoding.
1114
 * \param w the width of the window.
1115
 * \param h the height of the window.
1116
 * \param flags 0, or one or more SDL_WindowFlags OR'd together.
1117
 * \returns the window that was created or NULL on failure; call
1118
 *          SDL_GetError() for more information.
1119
 *
1120
 * \threadsafety This function should only be called on the main thread.
1121
 *
1122
 * \since This function is available since SDL 3.2.0.
1123
 *
1124
 * \sa SDL_CreateWindowAndRenderer
1125
 * \sa SDL_CreatePopupWindow
1126
 * \sa SDL_CreateWindowWithProperties
1127
 * \sa SDL_DestroyWindow
1128
 */
1129
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int w, int h, SDL_WindowFlags flags);
1130
1131
/**
1132
 * Create a child popup window of the specified parent window.
1133
 *
1134
 * The window size is a request and may be different than expected based on
1135
 * the desktop layout and window manager policies. Your application should be
1136
 * prepared to handle a window of any size.
1137
 *
1138
 * The flags parameter **must** contain at least one of the following:
1139
 *
1140
 * - `SDL_WINDOW_TOOLTIP`: The popup window is a tooltip and will not pass any
1141
 *   input events.
1142
 * - `SDL_WINDOW_POPUP_MENU`: The popup window is a popup menu. The topmost
1143
 *   popup menu will implicitly gain the keyboard focus.
1144
 *
1145
 * The following flags are not relevant to popup window creation and will be
1146
 * ignored:
1147
 *
1148
 * - `SDL_WINDOW_MINIMIZED`
1149
 * - `SDL_WINDOW_MAXIMIZED`
1150
 * - `SDL_WINDOW_FULLSCREEN`
1151
 * - `SDL_WINDOW_BORDERLESS`
1152
 *
1153
 * The following flags are incompatible with popup window creation and will
1154
 * cause it to fail:
1155
 *
1156
 * - `SDL_WINDOW_UTILITY`
1157
 * - `SDL_WINDOW_MODAL`
1158
 *
1159
 * The parent parameter **must** be non-null and a valid window. The parent of
1160
 * a popup window can be either a regular, toplevel window, or another popup
1161
 * window.
1162
 *
1163
 * Popup windows cannot be minimized, maximized, made fullscreen, raised,
1164
 * flash, be made a modal window, be the parent of a toplevel window, or grab
1165
 * the mouse and/or keyboard. Attempts to do so will fail.
1166
 *
1167
 * Popup windows implicitly do not have a border/decorations and do not appear
1168
 * on the taskbar/dock or in lists of windows such as alt-tab menus.
1169
 *
1170
 * By default, popup window positions will automatically be constrained to keep
1171
 * the entire window within display bounds. This can be overridden with the
1172
 * `SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN` property.
1173
 *
1174
 * By default, popup menus will automatically grab keyboard focus from the parent
1175
 * when shown. This behavior can be overridden by setting the `SDL_WINDOW_NOT_FOCUSABLE`
1176
 * flag, setting the `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN` property to false, or
1177
 * toggling it after creation via the `SDL_SetWindowFocusable()` function.
1178
 *
1179
 * If a parent window is hidden or destroyed, any child popup windows will be
1180
 * recursively hidden or destroyed as well. Child popup windows not explicitly
1181
 * hidden will be restored when the parent is shown.
1182
 *
1183
 * \param parent the parent of the window, must not be NULL.
1184
 * \param offset_x the x position of the popup window relative to the origin
1185
 *                 of the parent.
1186
 * \param offset_y the y position of the popup window relative to the origin
1187
 *                 of the parent window.
1188
 * \param w the width of the window.
1189
 * \param h the height of the window.
1190
 * \param flags SDL_WINDOW_TOOLTIP or SDL_WINDOW_POPUP_MENU, and zero or more
1191
 *              additional SDL_WindowFlags OR'd together.
1192
 * \returns the window that was created or NULL on failure; call
1193
 *          SDL_GetError() for more information.
1194
 *
1195
 * \threadsafety This function should only be called on the main thread.
1196
 *
1197
 * \since This function is available since SDL 3.2.0.
1198
 *
1199
 * \sa SDL_CreateWindow
1200
 * \sa SDL_CreateWindowWithProperties
1201
 * \sa SDL_DestroyWindow
1202
 * \sa SDL_GetWindowParent
1203
 */
1204
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *parent, int offset_x, int offset_y, int w, int h, SDL_WindowFlags flags);
1205
1206
/**
1207
 * Create a window with the specified properties.
1208
 *
1209
 * The window size is a request and may be different than expected based on
1210
 * the desktop layout and window manager policies. Your application should be
1211
 * prepared to handle a window of any size.
1212
 *
1213
 * These are the supported properties:
1214
 *
1215
 * - `SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN`: true if the window should
1216
 *   be always on top
1217
 * - `SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN`: true if the window has no
1218
 *   window decoration
1219
 * - `SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN`: true if the "tooltip" and
1220
 *   "menu" window types should be automatically constrained to be entirely within
1221
 *   display bounds (default), false if no constraints on the position are desired.
1222
 * - `SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN`: true if the
1223
 *   window will be used with an externally managed graphics context.
1224
 * - `SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN`: true if the window should
1225
 *   accept keyboard input (defaults true)
1226
 * - `SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN`: true if the window should
1227
 *   start in fullscreen mode at desktop resolution
1228
 * - `SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER`: the height of the window
1229
 * - `SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN`: true if the window should start
1230
 *   hidden
1231
 * - `SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN`: true if the window
1232
 *   uses a high pixel density buffer if possible
1233
 * - `SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN`: true if the window should
1234
 *   start maximized
1235
 * - `SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN`: true if the window is a popup menu
1236
 * - `SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN`: true if the window will be used
1237
 *   with Metal rendering
1238
 * - `SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN`: true if the window should
1239
 *   start minimized
1240
 * - `SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN`: true if the window is modal to
1241
 *   its parent
1242
 * - `SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN`: true if the window starts
1243
 *   with grabbed mouse focus
1244
 * - `SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN`: true if the window will be used
1245
 *   with OpenGL rendering
1246
 * - `SDL_PROP_WINDOW_CREATE_PARENT_POINTER`: an SDL_Window that will be the
1247
 *   parent of this window, required for windows with the "tooltip", "menu",
1248
 *   and "modal" properties
1249
 * - `SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN`: true if the window should be
1250
 *   resizable
1251
 * - `SDL_PROP_WINDOW_CREATE_TITLE_STRING`: the title of the window, in UTF-8
1252
 *   encoding
1253
 * - `SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN`: true if the window show
1254
 *   transparent in the areas with alpha of 0
1255
 * - `SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN`: true if the window is a tooltip
1256
 * - `SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN`: true if the window is a utility
1257
 *   window, not showing in the task bar and window list
1258
 * - `SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN`: true if the window will be used
1259
 *   with Vulkan rendering
1260
 * - `SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER`: the width of the window
1261
 * - `SDL_PROP_WINDOW_CREATE_X_NUMBER`: the x position of the window, or
1262
 *   `SDL_WINDOWPOS_CENTERED`, defaults to `SDL_WINDOWPOS_UNDEFINED`. This is
1263
 *   relative to the parent for windows with the "tooltip" or "menu" property
1264
 *   set.
1265
 * - `SDL_PROP_WINDOW_CREATE_Y_NUMBER`: the y position of the window, or
1266
 *   `SDL_WINDOWPOS_CENTERED`, defaults to `SDL_WINDOWPOS_UNDEFINED`. This is
1267
 *   relative to the parent for windows with the "tooltip" or "menu" property
1268
 *   set.
1269
 *
1270
 * These are additional supported properties on macOS:
1271
 *
1272
 * - `SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER`: the
1273
 *   `(__unsafe_unretained)` NSWindow associated with the window, if you want
1274
 *   to wrap an existing window.
1275
 * - `SDL_PROP_WINDOW_CREATE_COCOA_VIEW_POINTER`: the `(__unsafe_unretained)`
1276
 *   NSView associated with the window, defaults to `[window contentView]`
1277
 *
1278
 * These are additional supported properties on Wayland:
1279
 *
1280
 * - `SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN` - true if
1281
 *   the application wants to use the Wayland surface for a custom role and
1282
 *   does not want it attached to an XDG toplevel window. See
1283
 *   [README/wayland](README/wayland) for more information on using custom
1284
 *   surfaces.
1285
 * - `SDL_PROP_WINDOW_CREATE_WAYLAND_CREATE_EGL_WINDOW_BOOLEAN` - true if the
1286
 *   application wants an associated `wl_egl_window` object to be created and
1287
 *   attached to the window, even if the window does not have the OpenGL
1288
 *   property or `SDL_WINDOW_OPENGL` flag set.
1289
 * - `SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER` - the wl_surface
1290
 *   associated with the window, if you want to wrap an existing window. See
1291
 *   [README/wayland](README/wayland) for more information.
1292
 *
1293
 * These are additional supported properties on Windows:
1294
 *
1295
 * - `SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER`: the HWND associated with the
1296
 *   window, if you want to wrap an existing window.
1297
 * - `SDL_PROP_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER`: optional,
1298
 *   another window to share pixel format with, useful for OpenGL windows
1299
 *
1300
 * These are additional supported properties with X11:
1301
 *
1302
 * - `SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER`: the X11 Window associated
1303
 *   with the window, if you want to wrap an existing window.
1304
 *
1305
 * The window is implicitly shown if the "hidden" property is not set.
1306
 *
1307
 * Windows with the "tooltip" and "menu" properties are popup windows and have
1308
 * the behaviors and guidelines outlined in SDL_CreatePopupWindow().
1309
 *
1310
 * If this window is being created to be used with an SDL_Renderer, you should
1311
 * not add a graphics API specific property
1312
 * (`SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN`, etc), as SDL will handle that
1313
 * internally when it chooses a renderer. However, SDL might need to recreate
1314
 * your window at that point, which may cause the window to appear briefly,
1315
 * and then flicker as it is recreated. The correct approach to this is to
1316
 * create the window with the `SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN` property
1317
 * set to true, then create the renderer, then show the window with
1318
 * SDL_ShowWindow().
1319
 *
1320
 * \param props the properties to use.
1321
 * \returns the window that was created or NULL on failure; call
1322
 *          SDL_GetError() for more information.
1323
 *
1324
 * \threadsafety This function should only be called on the main thread.
1325
 *
1326
 * \since This function is available since SDL 3.2.0.
1327
 *
1328
 * \sa SDL_CreateProperties
1329
 * \sa SDL_CreateWindow
1330
 * \sa SDL_DestroyWindow
1331
 */
1332
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowWithProperties(SDL_PropertiesID props);
1333
1334
#define SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN               "SDL.window.create.always_on_top"
1335
#define SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN                  "SDL.window.create.borderless"
1336
#define SDL_PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN             "SDL.window.create.constrain_popup"
1337
#define SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN                   "SDL.window.create.focusable"
1338
#define SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN   "SDL.window.create.external_graphics_context"
1339
#define SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER                        "SDL.window.create.flags"
1340
#define SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN                  "SDL.window.create.fullscreen"
1341
#define SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER                       "SDL.window.create.height"
1342
#define SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN                      "SDL.window.create.hidden"
1343
#define SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN          "SDL.window.create.high_pixel_density"
1344
#define SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN                   "SDL.window.create.maximized"
1345
#define SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN                        "SDL.window.create.menu"
1346
#define SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN                       "SDL.window.create.metal"
1347
#define SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN                   "SDL.window.create.minimized"
1348
#define SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN                       "SDL.window.create.modal"
1349
#define SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN               "SDL.window.create.mouse_grabbed"
1350
#define SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN                      "SDL.window.create.opengl"
1351
#define SDL_PROP_WINDOW_CREATE_PARENT_POINTER                      "SDL.window.create.parent"
1352
#define SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN                   "SDL.window.create.resizable"
1353
#define SDL_PROP_WINDOW_CREATE_TITLE_STRING                        "SDL.window.create.title"
1354
#define SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN                 "SDL.window.create.transparent"
1355
#define SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN                     "SDL.window.create.tooltip"
1356
#define SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN                     "SDL.window.create.utility"
1357
#define SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN                      "SDL.window.create.vulkan"
1358
#define SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER                        "SDL.window.create.width"
1359
#define SDL_PROP_WINDOW_CREATE_X_NUMBER                            "SDL.window.create.x"
1360
#define SDL_PROP_WINDOW_CREATE_Y_NUMBER                            "SDL.window.create.y"
1361
#define SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER                "SDL.window.create.cocoa.window"
1362
#define SDL_PROP_WINDOW_CREATE_COCOA_VIEW_POINTER                  "SDL.window.create.cocoa.view"
1363
#define SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN "SDL.window.create.wayland.surface_role_custom"
1364
#define SDL_PROP_WINDOW_CREATE_WAYLAND_CREATE_EGL_WINDOW_BOOLEAN   "SDL.window.create.wayland.create_egl_window"
1365
#define SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER          "SDL.window.create.wayland.wl_surface"
1366
#define SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER                  "SDL.window.create.win32.hwnd"
1367
#define SDL_PROP_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER     "SDL.window.create.win32.pixel_format_hwnd"
1368
#define SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER                   "SDL.window.create.x11.window"
1369
1370
/**
1371
 * Get the numeric ID of a window.
1372
 *
1373
 * The numeric ID is what SDL_WindowEvent references, and is necessary to map
1374
 * these events to specific SDL_Window objects.
1375
 *
1376
 * \param window the window to query.
1377
 * \returns the ID of the window on success or 0 on failure; call
1378
 *          SDL_GetError() for more information.
1379
 *
1380
 * \threadsafety This function should only be called on the main thread.
1381
 *
1382
 * \since This function is available since SDL 3.2.0.
1383
 *
1384
 * \sa SDL_GetWindowFromID
1385
 */
1386
extern SDL_DECLSPEC SDL_WindowID SDLCALL SDL_GetWindowID(SDL_Window *window);
1387
1388
/**
1389
 * Get a window from a stored ID.
1390
 *
1391
 * The numeric ID is what SDL_WindowEvent references, and is necessary to map
1392
 * these events to specific SDL_Window objects.
1393
 *
1394
 * \param id the ID of the window.
1395
 * \returns the window associated with `id` or NULL if it doesn't exist; call
1396
 *          SDL_GetError() for more information.
1397
 *
1398
 * \threadsafety This function should only be called on the main thread.
1399
 *
1400
 * \since This function is available since SDL 3.2.0.
1401
 *
1402
 * \sa SDL_GetWindowID
1403
 */
1404
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowFromID(SDL_WindowID id);
1405
1406
/**
1407
 * Get parent of a window.
1408
 *
1409
 * \param window the window to query.
1410
 * \returns the parent of the window on success or NULL if the window has no
1411
 *          parent.
1412
 *
1413
 * \threadsafety This function should only be called on the main thread.
1414
 *
1415
 * \since This function is available since SDL 3.2.0.
1416
 *
1417
 * \sa SDL_CreatePopupWindow
1418
 */
1419
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window);
1420
1421
/**
1422
 * Get the properties associated with a window.
1423
 *
1424
 * The following read-only properties are provided by SDL:
1425
 *
1426
 * - `SDL_PROP_WINDOW_SHAPE_POINTER`: the surface associated with a shaped
1427
 *   window
1428
 * - `SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN`: true if the window has HDR
1429
 *   headroom above the SDR white point. This property can change dynamically
1430
 *   when SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent.
1431
 * - `SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT`: the value of SDR white in the
1432
 *   SDL_COLORSPACE_SRGB_LINEAR colorspace. On Windows this corresponds to the
1433
 *   SDR white level in scRGB colorspace, and on Apple platforms this is
1434
 *   always 1.0 for EDR content. This property can change dynamically when
1435
 *   SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent.
1436
 * - `SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT`: the additional high dynamic range
1437
 *   that can be displayed, in terms of the SDR white point. When HDR is not
1438
 *   enabled, this will be 1.0. This property can change dynamically when
1439
 *   SDL_EVENT_WINDOW_HDR_STATE_CHANGED is sent.
1440
 *
1441
 * On Android:
1442
 *
1443
 * - `SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER`: the ANativeWindow associated
1444
 *   with the window
1445
 * - `SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER`: the EGLSurface associated with
1446
 *   the window
1447
 *
1448
 * On iOS:
1449
 *
1450
 * - `SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER`: the `(__unsafe_unretained)`
1451
 *   UIWindow associated with the window
1452
 * - `SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER`: the NSInteger tag
1453
 *   associated with metal views on the window
1454
 * - `SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER`: the OpenGL view's
1455
 *   framebuffer object. It must be bound when rendering to the screen using
1456
 *   OpenGL.
1457
 * - `SDL_PROP_WINDOW_UIKIT_OPENGL_RENDERBUFFER_NUMBER`: the OpenGL view's
1458
 *   renderbuffer object. It must be bound when SDL_GL_SwapWindow is called.
1459
 * - `SDL_PROP_WINDOW_UIKIT_OPENGL_RESOLVE_FRAMEBUFFER_NUMBER`: the OpenGL
1460
 *   view's resolve framebuffer, when MSAA is used.
1461
 *
1462
 * On KMS/DRM:
1463
 *
1464
 * - `SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER`: the device index associated
1465
 *   with the window (e.g. the X in /dev/dri/cardX)
1466
 * - `SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER`: the DRM FD associated with the
1467
 *   window
1468
 * - `SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER`: the GBM device associated
1469
 *   with the window
1470
 *
1471
 * On macOS:
1472
 *
1473
 * - `SDL_PROP_WINDOW_COCOA_WINDOW_POINTER`: the `(__unsafe_unretained)`
1474
 *   NSWindow associated with the window
1475
 * - `SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER`: the NSInteger tag
1476
 *   associated with metal views on the window
1477
 *
1478
 * On OpenVR:
1479
 *
1480
 * - `SDL_PROP_WINDOW_OPENVR_OVERLAY_ID`: the OpenVR Overlay Handle ID for the
1481
 *   associated overlay window.
1482
 *
1483
 * On Vivante:
1484
 *
1485
 * - `SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER`: the EGLNativeDisplayType
1486
 *   associated with the window
1487
 * - `SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER`: the EGLNativeWindowType
1488
 *   associated with the window
1489
 * - `SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER`: the EGLSurface associated with
1490
 *   the window
1491
 *
1492
 * On Windows:
1493
 *
1494
 * - `SDL_PROP_WINDOW_WIN32_HWND_POINTER`: the HWND associated with the window
1495
 * - `SDL_PROP_WINDOW_WIN32_HDC_POINTER`: the HDC associated with the window
1496
 * - `SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER`: the HINSTANCE associated with
1497
 *   the window
1498
 *
1499
 * On Wayland:
1500
 *
1501
 * Note: The `xdg_*` window objects do not internally persist across window
1502
 * show/hide calls. They will be null if the window is hidden and must be
1503
 * queried each time it is shown.
1504
 *
1505
 * - `SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER`: the wl_display associated with
1506
 *   the window
1507
 * - `SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER`: the wl_surface associated with
1508
 *   the window
1509
 * - `SDL_PROP_WINDOW_WAYLAND_VIEWPORT_POINTER`: the wp_viewport associated
1510
 *   with the window
1511
 * - `SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER`: the wl_egl_window
1512
 *   associated with the window
1513
 * - `SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER`: the xdg_surface associated
1514
 *   with the window
1515
 * - `SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER`: the xdg_toplevel role
1516
 *   associated with the window
1517
 * - 'SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING': the export
1518
 *   handle associated with the window
1519
 * - `SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER`: the xdg_popup role
1520
 *   associated with the window
1521
 * - `SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER`: the xdg_positioner
1522
 *   associated with the window, in popup mode
1523
 *
1524
 * On X11:
1525
 *
1526
 * - `SDL_PROP_WINDOW_X11_DISPLAY_POINTER`: the X11 Display associated with
1527
 *   the window
1528
 * - `SDL_PROP_WINDOW_X11_SCREEN_NUMBER`: the screen number associated with
1529
 *   the window
1530
 * - `SDL_PROP_WINDOW_X11_WINDOW_NUMBER`: the X11 Window associated with the
1531
 *   window
1532
 *
1533
 * \param window the window to query.
1534
 * \returns a valid property ID on success or 0 on failure; call
1535
 *          SDL_GetError() for more information.
1536
 *
1537
 * \threadsafety This function should only be called on the main thread.
1538
 *
1539
 * \since This function is available since SDL 3.2.0.
1540
 */
1541
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window *window);
1542
1543
#define SDL_PROP_WINDOW_SHAPE_POINTER                               "SDL.window.shape"
1544
#define SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN                         "SDL.window.HDR_enabled"
1545
#define SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT                       "SDL.window.SDR_white_level"
1546
#define SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT                          "SDL.window.HDR_headroom"
1547
#define SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER                      "SDL.window.android.window"
1548
#define SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER                     "SDL.window.android.surface"
1549
#define SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER                        "SDL.window.uikit.window"
1550
#define SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER                 "SDL.window.uikit.metal_view_tag"
1551
#define SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER             "SDL.window.uikit.opengl.framebuffer"
1552
#define SDL_PROP_WINDOW_UIKIT_OPENGL_RENDERBUFFER_NUMBER            "SDL.window.uikit.opengl.renderbuffer"
1553
#define SDL_PROP_WINDOW_UIKIT_OPENGL_RESOLVE_FRAMEBUFFER_NUMBER     "SDL.window.uikit.opengl.resolve_framebuffer"
1554
#define SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER                  "SDL.window.kmsdrm.dev_index"
1555
#define SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER                        "SDL.window.kmsdrm.drm_fd"
1556
#define SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER                   "SDL.window.kmsdrm.gbm_dev"
1557
1
#define SDL_PROP_WINDOW_COCOA_WINDOW_POINTER                        "SDL.window.cocoa.window"
1558
#define SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER                 "SDL.window.cocoa.metal_view_tag"
1559
#define SDL_PROP_WINDOW_OPENVR_OVERLAY_ID                           "SDL.window.openvr.overlay_id"
1560
#define SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER                     "SDL.window.vivante.display"
1561
#define SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER                      "SDL.window.vivante.window"
1562
#define SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER                     "SDL.window.vivante.surface"
1563
#define SDL_PROP_WINDOW_WIN32_HWND_POINTER                          "SDL.window.win32.hwnd"
1564
#define SDL_PROP_WINDOW_WIN32_HDC_POINTER                           "SDL.window.win32.hdc"
1565
#define SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER                      "SDL.window.win32.instance"
1566
#define SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER                     "SDL.window.wayland.display"
1567
#define SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER                     "SDL.window.wayland.surface"
1568
#define SDL_PROP_WINDOW_WAYLAND_VIEWPORT_POINTER                    "SDL.window.wayland.viewport"
1569
#define SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER                  "SDL.window.wayland.egl_window"
1570
#define SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER                 "SDL.window.wayland.xdg_surface"
1571
#define SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER                "SDL.window.wayland.xdg_toplevel"
1572
#define SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING   "SDL.window.wayland.xdg_toplevel_export_handle"
1573
#define SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER                   "SDL.window.wayland.xdg_popup"
1574
#define SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER              "SDL.window.wayland.xdg_positioner"
1575
#define SDL_PROP_WINDOW_X11_DISPLAY_POINTER                         "SDL.window.x11.display"
1576
#define SDL_PROP_WINDOW_X11_SCREEN_NUMBER                           "SDL.window.x11.screen"
1577
#define SDL_PROP_WINDOW_X11_WINDOW_NUMBER                           "SDL.window.x11.window"
1578
1579
/**
1580
 * Get the window flags.
1581
 *
1582
 * \param window the window to query.
1583
 * \returns a mask of the SDL_WindowFlags associated with `window`.
1584
 *
1585
 * \threadsafety This function should only be called on the main thread.
1586
 *
1587
 * \since This function is available since SDL 3.2.0.
1588
 *
1589
 * \sa SDL_CreateWindow
1590
 * \sa SDL_HideWindow
1591
 * \sa SDL_MaximizeWindow
1592
 * \sa SDL_MinimizeWindow
1593
 * \sa SDL_SetWindowFullscreen
1594
 * \sa SDL_SetWindowMouseGrab
1595
 * \sa SDL_ShowWindow
1596
 */
1597
extern SDL_DECLSPEC SDL_WindowFlags SDLCALL SDL_GetWindowFlags(SDL_Window *window);
1598
1599
/**
1600
 * Set the title of a window.
1601
 *
1602
 * This string is expected to be in UTF-8 encoding.
1603
 *
1604
 * \param window the window to change.
1605
 * \param title the desired window title in UTF-8 format.
1606
 * \returns true on success or false on failure; call SDL_GetError() for more
1607
 *          information.
1608
 *
1609
 * \threadsafety This function should only be called on the main thread.
1610
 *
1611
 * \since This function is available since SDL 3.2.0.
1612
 *
1613
 * \sa SDL_GetWindowTitle
1614
 */
1615
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowTitle(SDL_Window *window, const char *title);
1616
1617
/**
1618
 * Get the title of a window.
1619
 *
1620
 * \param window the window to query.
1621
 * \returns the title of the window in UTF-8 format or "" if there is no
1622
 *          title.
1623
 *
1624
 * \threadsafety This function should only be called on the main thread.
1625
 *
1626
 * \since This function is available since SDL 3.2.0.
1627
 *
1628
 * \sa SDL_SetWindowTitle
1629
 */
1630
extern SDL_DECLSPEC const char * SDLCALL SDL_GetWindowTitle(SDL_Window *window);
1631
1632
/**
1633
 * Set the icon for a window.
1634
 *
1635
 * If this function is passed a surface with alternate representations, the
1636
 * surface will be interpreted as the content to be used for 100% display
1637
 * scale, and the alternate representations will be used for high DPI
1638
 * situations. For example, if the original surface is 32x32, then on a 2x
1639
 * macOS display or 200% display scale on Windows, a 64x64 version of the
1640
 * image will be used, if available. If a matching version of the image isn't
1641
 * available, the closest larger size image will be downscaled to the
1642
 * appropriate size and be used instead, if available. Otherwise, the closest
1643
 * smaller image will be upscaled and be used instead.
1644
 *
1645
 * \param window the window to change.
1646
 * \param icon an SDL_Surface structure containing the icon for the window.
1647
 * \returns true on success or false on failure; call SDL_GetError() for more
1648
 *          information.
1649
 *
1650
 * \threadsafety This function should only be called on the main thread.
1651
 *
1652
 * \since This function is available since SDL 3.2.0.
1653
 */
1654
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowIcon(SDL_Window *window, SDL_Surface *icon);
1655
1656
/**
1657
 * Request that the window's position be set.
1658
 *
1659
 * If the window is in an exclusive fullscreen or maximized state, this
1660
 * request has no effect.
1661
 *
1662
 * This can be used to reposition fullscreen-desktop windows onto a different
1663
 * display, however, as exclusive fullscreen windows are locked to a specific
1664
 * display, they can only be repositioned programmatically via
1665
 * SDL_SetWindowFullscreenMode().
1666
 *
1667
 * On some windowing systems this request is asynchronous and the new
1668
 * coordinates may not have have been applied immediately upon the return of
1669
 * this function. If an immediate change is required, call SDL_SyncWindow() to
1670
 * block until the changes have taken effect.
1671
 *
1672
 * When the window position changes, an SDL_EVENT_WINDOW_MOVED event will be
1673
 * emitted with the window's new coordinates. Note that the new coordinates
1674
 * may not match the exact coordinates requested, as some windowing systems
1675
 * can restrict the position of the window in certain scenarios (e.g.
1676
 * constraining the position so the window is always within desktop bounds).
1677
 * Additionally, as this is just a request, it can be denied by the windowing
1678
 * system.
1679
 *
1680
 * \param window the window to reposition.
1681
 * \param x the x coordinate of the window, or `SDL_WINDOWPOS_CENTERED` or
1682
 *          `SDL_WINDOWPOS_UNDEFINED`.
1683
 * \param y the y coordinate of the window, or `SDL_WINDOWPOS_CENTERED` or
1684
 *          `SDL_WINDOWPOS_UNDEFINED`.
1685
 * \returns true on success or false on failure; call SDL_GetError() for more
1686
 *          information.
1687
 *
1688
 * \threadsafety This function should only be called on the main thread.
1689
 *
1690
 * \since This function is available since SDL 3.2.0.
1691
 *
1692
 * \sa SDL_GetWindowPosition
1693
 * \sa SDL_SyncWindow
1694
 */
1695
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowPosition(SDL_Window *window, int x, int y);
1696
1697
/**
1698
 * Get the position of a window.
1699
 *
1700
 * This is the current position of the window as last reported by the
1701
 * windowing system.
1702
 *
1703
 * If you do not need the value for one of the positions a NULL may be passed
1704
 * in the `x` or `y` parameter.
1705
 *
1706
 * \param window the window to query.
1707
 * \param x a pointer filled in with the x position of the window, may be
1708
 *          NULL.
1709
 * \param y a pointer filled in with the y position of the window, may be
1710
 *          NULL.
1711
 * \returns true on success or false on failure; call SDL_GetError() for more
1712
 *          information.
1713
 *
1714
 * \threadsafety This function should only be called on the main thread.
1715
 *
1716
 * \since This function is available since SDL 3.2.0.
1717
 *
1718
 * \sa SDL_SetWindowPosition
1719
 */
1720
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowPosition(SDL_Window *window, int *x, int *y);
1721
1722
/**
1723
 * Request that the size of a window's client area be set.
1724
 *
1725
 * If the window is in a fullscreen or maximized state, this request has no
1726
 * effect.
1727
 *
1728
 * To change the exclusive fullscreen mode of a window, use
1729
 * SDL_SetWindowFullscreenMode().
1730
 *
1731
 * On some windowing systems, this request is asynchronous and the new window
1732
 * size may not have have been applied immediately upon the return of this
1733
 * function. If an immediate change is required, call SDL_SyncWindow() to
1734
 * block until the changes have taken effect.
1735
 *
1736
 * When the window size changes, an SDL_EVENT_WINDOW_RESIZED event will be
1737
 * emitted with the new window dimensions. Note that the new dimensions may
1738
 * not match the exact size requested, as some windowing systems can restrict
1739
 * the window size in certain scenarios (e.g. constraining the size of the
1740
 * content area to remain within the usable desktop bounds). Additionally, as
1741
 * this is just a request, it can be denied by the windowing system.
1742
 *
1743
 * \param window the window to change.
1744
 * \param w the width of the window, must be > 0.
1745
 * \param h the height of the window, must be > 0.
1746
 * \returns true on success or false on failure; call SDL_GetError() for more
1747
 *          information.
1748
 *
1749
 * \threadsafety This function should only be called on the main thread.
1750
 *
1751
 * \since This function is available since SDL 3.2.0.
1752
 *
1753
 * \sa SDL_GetWindowSize
1754
 * \sa SDL_SetWindowFullscreenMode
1755
 * \sa SDL_SyncWindow
1756
 */
1757
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowSize(SDL_Window *window, int w, int h);
1758
1759
/**
1760
 * Get the size of a window's client area.
1761
 *
1762
 * The window pixel size may differ from its window coordinate size if the
1763
 * window is on a high pixel density display. Use SDL_GetWindowSizeInPixels()
1764
 * or SDL_GetRenderOutputSize() to get the real client area size in pixels.
1765
 *
1766
 * \param window the window to query the width and height from.
1767
 * \param w a pointer filled in with the width of the window, may be NULL.
1768
 * \param h a pointer filled in with the height of the window, may be NULL.
1769
 * \returns true on success or false on failure; call SDL_GetError() for more
1770
 *          information.
1771
 *
1772
 * \threadsafety This function should only be called on the main thread.
1773
 *
1774
 * \since This function is available since SDL 3.2.0.
1775
 *
1776
 * \sa SDL_GetRenderOutputSize
1777
 * \sa SDL_GetWindowSizeInPixels
1778
 * \sa SDL_SetWindowSize
1779
 */
1780
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
1781
1782
/**
1783
 * Get the safe area for this window.
1784
 *
1785
 * Some devices have portions of the screen which are partially obscured or
1786
 * not interactive, possibly due to on-screen controls, curved edges, camera
1787
 * notches, TV overscan, etc. This function provides the area of the window
1788
 * which is safe to have interactable content. You should continue rendering
1789
 * into the rest of the window, but it should not contain visually important
1790
 * or interactible content.
1791
 *
1792
 * \param window the window to query.
1793
 * \param rect a pointer filled in with the client area that is safe for
1794
 *             interactive content.
1795
 * \returns true on success or false on failure; call SDL_GetError() for more
1796
 *          information.
1797
 *
1798
 * \threadsafety This function should only be called on the main thread.
1799
 *
1800
 * \since This function is available since SDL 3.2.0.
1801
 */
1802
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSafeArea(SDL_Window *window, SDL_Rect *rect);
1803
1804
/**
1805
 * Request that the aspect ratio of a window's client area be set.
1806
 *
1807
 * The aspect ratio is the ratio of width divided by height, e.g. 2560x1600
1808
 * would be 1.6. Larger aspect ratios are wider and smaller aspect ratios are
1809
 * narrower.
1810
 *
1811
 * If, at the time of this request, the window in a fixed-size state, such as
1812
 * maximized or fullscreen, the request will be deferred until the window
1813
 * exits this state and becomes resizable again.
1814
 *
1815
 * On some windowing systems, this request is asynchronous and the new window
1816
 * aspect ratio may not have have been applied immediately upon the return of
1817
 * this function. If an immediate change is required, call SDL_SyncWindow() to
1818
 * block until the changes have taken effect.
1819
 *
1820
 * When the window size changes, an SDL_EVENT_WINDOW_RESIZED event will be
1821
 * emitted with the new window dimensions. Note that the new dimensions may
1822
 * not match the exact aspect ratio requested, as some windowing systems can
1823
 * restrict the window size in certain scenarios (e.g. constraining the size
1824
 * of the content area to remain within the usable desktop bounds).
1825
 * Additionally, as this is just a request, it can be denied by the windowing
1826
 * system.
1827
 *
1828
 * \param window the window to change.
1829
 * \param min_aspect the minimum aspect ratio of the window, or 0.0f for no
1830
 *                   limit.
1831
 * \param max_aspect the maximum aspect ratio of the window, or 0.0f for no
1832
 *                   limit.
1833
 * \returns true on success or false on failure; call SDL_GetError() for more
1834
 *          information.
1835
 *
1836
 * \threadsafety This function should only be called on the main thread.
1837
 *
1838
 * \since This function is available since SDL 3.2.0.
1839
 *
1840
 * \sa SDL_GetWindowAspectRatio
1841
 * \sa SDL_SyncWindow
1842
 */
1843
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowAspectRatio(SDL_Window *window, float min_aspect, float max_aspect);
1844
1845
/**
1846
 * Get the size of a window's client area.
1847
 *
1848
 * \param window the window to query the width and height from.
1849
 * \param min_aspect a pointer filled in with the minimum aspect ratio of the
1850
 *                   window, may be NULL.
1851
 * \param max_aspect a pointer filled in with the maximum aspect ratio of the
1852
 *                   window, may be NULL.
1853
 * \returns true on success or false on failure; call SDL_GetError() for more
1854
 *          information.
1855
 *
1856
 * \threadsafety This function should only be called on the main thread.
1857
 *
1858
 * \since This function is available since SDL 3.2.0.
1859
 *
1860
 * \sa SDL_SetWindowAspectRatio
1861
 */
1862
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowAspectRatio(SDL_Window *window, float *min_aspect, float *max_aspect);
1863
1864
/**
1865
 * Get the size of a window's borders (decorations) around the client area.
1866
 *
1867
 * Note: If this function fails (returns false), the size values will be
1868
 * initialized to 0, 0, 0, 0 (if a non-NULL pointer is provided), as if the
1869
 * window in question was borderless.
1870
 *
1871
 * Note: This function may fail on systems where the window has not yet been
1872
 * decorated by the display server (for example, immediately after calling
1873
 * SDL_CreateWindow). It is recommended that you wait at least until the
1874
 * window has been presented and composited, so that the window system has a
1875
 * chance to decorate the window and provide the border dimensions to SDL.
1876
 *
1877
 * This function also returns false if getting the information is not
1878
 * supported.
1879
 *
1880
 * \param window the window to query the size values of the border
1881
 *               (decorations) from.
1882
 * \param top pointer to variable for storing the size of the top border; NULL
1883
 *            is permitted.
1884
 * \param left pointer to variable for storing the size of the left border;
1885
 *             NULL is permitted.
1886
 * \param bottom pointer to variable for storing the size of the bottom
1887
 *               border; NULL is permitted.
1888
 * \param right pointer to variable for storing the size of the right border;
1889
 *              NULL is permitted.
1890
 * \returns true on success or false on failure; call SDL_GetError() for more
1891
 *          information.
1892
 *
1893
 * \threadsafety This function should only be called on the main thread.
1894
 *
1895
 * \since This function is available since SDL 3.2.0.
1896
 *
1897
 * \sa SDL_GetWindowSize
1898
 */
1899
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowBordersSize(SDL_Window *window, int *top, int *left, int *bottom, int *right);
1900
1901
/**
1902
 * Get the size of a window's client area, in pixels.
1903
 *
1904
 * \param window the window from which the drawable size should be queried.
1905
 * \param w a pointer to variable for storing the width in pixels, may be
1906
 *          NULL.
1907
 * \param h a pointer to variable for storing the height in pixels, may be
1908
 *          NULL.
1909
 * \returns true on success or false on failure; call SDL_GetError() for more
1910
 *          information.
1911
 *
1912
 * \threadsafety This function should only be called on the main thread.
1913
 *
1914
 * \since This function is available since SDL 3.2.0.
1915
 *
1916
 * \sa SDL_CreateWindow
1917
 * \sa SDL_GetWindowSize
1918
 */
1919
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSizeInPixels(SDL_Window *window, int *w, int *h);
1920
1921
/**
1922
 * Set the minimum size of a window's client area.
1923
 *
1924
 * \param window the window to change.
1925
 * \param min_w the minimum width of the window, or 0 for no limit.
1926
 * \param min_h the minimum height of the window, or 0 for no limit.
1927
 * \returns true on success or false on failure; call SDL_GetError() for more
1928
 *          information.
1929
 *
1930
 * \threadsafety This function should only be called on the main thread.
1931
 *
1932
 * \since This function is available since SDL 3.2.0.
1933
 *
1934
 * \sa SDL_GetWindowMinimumSize
1935
 * \sa SDL_SetWindowMaximumSize
1936
 */
1937
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h);
1938
1939
/**
1940
 * Get the minimum size of a window's client area.
1941
 *
1942
 * \param window the window to query.
1943
 * \param w a pointer filled in with the minimum width of the window, may be
1944
 *          NULL.
1945
 * \param h a pointer filled in with the minimum height of the window, may be
1946
 *          NULL.
1947
 * \returns true on success or false on failure; call SDL_GetError() for more
1948
 *          information.
1949
 *
1950
 * \threadsafety This function should only be called on the main thread.
1951
 *
1952
 * \since This function is available since SDL 3.2.0.
1953
 *
1954
 * \sa SDL_GetWindowMaximumSize
1955
 * \sa SDL_SetWindowMinimumSize
1956
 */
1957
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMinimumSize(SDL_Window *window, int *w, int *h);
1958
1959
/**
1960
 * Set the maximum size of a window's client area.
1961
 *
1962
 * \param window the window to change.
1963
 * \param max_w the maximum width of the window, or 0 for no limit.
1964
 * \param max_h the maximum height of the window, or 0 for no limit.
1965
 * \returns true on success or false on failure; call SDL_GetError() for more
1966
 *          information.
1967
 *
1968
 * \threadsafety This function should only be called on the main thread.
1969
 *
1970
 * \since This function is available since SDL 3.2.0.
1971
 *
1972
 * \sa SDL_GetWindowMaximumSize
1973
 * \sa SDL_SetWindowMinimumSize
1974
 */
1975
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMaximumSize(SDL_Window *window, int max_w, int max_h);
1976
1977
/**
1978
 * Get the maximum size of a window's client area.
1979
 *
1980
 * \param window the window to query.
1981
 * \param w a pointer filled in with the maximum width of the window, may be
1982
 *          NULL.
1983
 * \param h a pointer filled in with the maximum height of the window, may be
1984
 *          NULL.
1985
 * \returns true on success or false on failure; call SDL_GetError() for more
1986
 *          information.
1987
 *
1988
 * \threadsafety This function should only be called on the main thread.
1989
 *
1990
 * \since This function is available since SDL 3.2.0.
1991
 *
1992
 * \sa SDL_GetWindowMinimumSize
1993
 * \sa SDL_SetWindowMaximumSize
1994
 */
1995
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMaximumSize(SDL_Window *window, int *w, int *h);
1996
1997
/**
1998
 * Set the border state of a window.
1999
 *
2000
 * This will add or remove the window's `SDL_WINDOW_BORDERLESS` flag and add
2001
 * or remove the border from the actual window. This is a no-op if the
2002
 * window's border already matches the requested state.
2003
 *
2004
 * You can't change the border state of a fullscreen window.
2005
 *
2006
 * \param window the window of which to change the border state.
2007
 * \param bordered false to remove border, true to add border.
2008
 * \returns true on success or false on failure; call SDL_GetError() for more
2009
 *          information.
2010
 *
2011
 * \threadsafety This function should only be called on the main thread.
2012
 *
2013
 * \since This function is available since SDL 3.2.0.
2014
 *
2015
 * \sa SDL_GetWindowFlags
2016
 */
2017
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowBordered(SDL_Window *window, bool bordered);
2018
2019
/**
2020
 * Set the user-resizable state of a window.
2021
 *
2022
 * This will add or remove the window's `SDL_WINDOW_RESIZABLE` flag and
2023
 * allow/disallow user resizing of the window. This is a no-op if the window's
2024
 * resizable state already matches the requested state.
2025
 *
2026
 * You can't change the resizable state of a fullscreen window.
2027
 *
2028
 * \param window the window of which to change the resizable state.
2029
 * \param resizable true to allow resizing, false to disallow.
2030
 * \returns true on success or false on failure; call SDL_GetError() for more
2031
 *          information.
2032
 *
2033
 * \threadsafety This function should only be called on the main thread.
2034
 *
2035
 * \since This function is available since SDL 3.2.0.
2036
 *
2037
 * \sa SDL_GetWindowFlags
2038
 */
2039
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowResizable(SDL_Window *window, bool resizable);
2040
2041
/**
2042
 * Set the window to always be above the others.
2043
 *
2044
 * This will add or remove the window's `SDL_WINDOW_ALWAYS_ON_TOP` flag. This
2045
 * will bring the window to the front and keep the window above the rest.
2046
 *
2047
 * \param window the window of which to change the always on top state.
2048
 * \param on_top true to set the window always on top, false to disable.
2049
 * \returns true on success or false on failure; call SDL_GetError() for more
2050
 *          information.
2051
 *
2052
 * \threadsafety This function should only be called on the main thread.
2053
 *
2054
 * \since This function is available since SDL 3.2.0.
2055
 *
2056
 * \sa SDL_GetWindowFlags
2057
 */
2058
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowAlwaysOnTop(SDL_Window *window, bool on_top);
2059
2060
/**
2061
 * Show a window.
2062
 *
2063
 * \param window the window to show.
2064
 * \returns true on success or false on failure; call SDL_GetError() for more
2065
 *          information.
2066
 *
2067
 * \threadsafety This function should only be called on the main thread.
2068
 *
2069
 * \since This function is available since SDL 3.2.0.
2070
 *
2071
 * \sa SDL_HideWindow
2072
 * \sa SDL_RaiseWindow
2073
 */
2074
extern SDL_DECLSPEC bool SDLCALL SDL_ShowWindow(SDL_Window *window);
2075
2076
/**
2077
 * Hide a window.
2078
 *
2079
 * \param window the window to hide.
2080
 * \returns true on success or false on failure; call SDL_GetError() for more
2081
 *          information.
2082
 *
2083
 * \threadsafety This function should only be called on the main thread.
2084
 *
2085
 * \since This function is available since SDL 3.2.0.
2086
 *
2087
 * \sa SDL_ShowWindow
2088
 * \sa SDL_WINDOW_HIDDEN
2089
 */
2090
extern SDL_DECLSPEC bool SDLCALL SDL_HideWindow(SDL_Window *window);
2091
2092
/**
2093
 * Request that a window be raised above other windows and gain the input
2094
 * focus.
2095
 *
2096
 * The result of this request is subject to desktop window manager policy,
2097
 * particularly if raising the requested window would result in stealing focus
2098
 * from another application. If the window is successfully raised and gains
2099
 * input focus, an SDL_EVENT_WINDOW_FOCUS_GAINED event will be emitted, and
2100
 * the window will have the SDL_WINDOW_INPUT_FOCUS flag set.
2101
 *
2102
 * \param window the window to raise.
2103
 * \returns true on success or false on failure; call SDL_GetError() for more
2104
 *          information.
2105
 *
2106
 * \threadsafety This function should only be called on the main thread.
2107
 *
2108
 * \since This function is available since SDL 3.2.0.
2109
 */
2110
extern SDL_DECLSPEC bool SDLCALL SDL_RaiseWindow(SDL_Window *window);
2111
2112
/**
2113
 * Request that the window be made as large as possible.
2114
 *
2115
 * Non-resizable windows can't be maximized. The window must have the
2116
 * SDL_WINDOW_RESIZABLE flag set, or this will have no effect.
2117
 *
2118
 * On some windowing systems this request is asynchronous and the new window
2119
 * state may not have have been applied immediately upon the return of this
2120
 * function. If an immediate change is required, call SDL_SyncWindow() to
2121
 * block until the changes have taken effect.
2122
 *
2123
 * When the window state changes, an SDL_EVENT_WINDOW_MAXIMIZED event will be
2124
 * emitted. Note that, as this is just a request, the windowing system can
2125
 * deny the state change.
2126
 *
2127
 * When maximizing a window, whether the constraints set via
2128
 * SDL_SetWindowMaximumSize() are honored depends on the policy of the window
2129
 * manager. Win32 and macOS enforce the constraints when maximizing, while X11
2130
 * and Wayland window managers may vary.
2131
 *
2132
 * \param window the window to maximize.
2133
 * \returns true on success or false on failure; call SDL_GetError() for more
2134
 *          information.
2135
 *
2136
 * \threadsafety This function should only be called on the main thread.
2137
 *
2138
 * \since This function is available since SDL 3.2.0.
2139
 *
2140
 * \sa SDL_MinimizeWindow
2141
 * \sa SDL_RestoreWindow
2142
 * \sa SDL_SyncWindow
2143
 */
2144
extern SDL_DECLSPEC bool SDLCALL SDL_MaximizeWindow(SDL_Window *window);
2145
2146
/**
2147
 * Request that the window be minimized to an iconic representation.
2148
 *
2149
 * If the window is in a fullscreen state, this request has no direct effect.
2150
 * It may alter the state the window is returned to when leaving fullscreen.
2151
 *
2152
 * On some windowing systems this request is asynchronous and the new window
2153
 * state may not have been applied immediately upon the return of this
2154
 * function. If an immediate change is required, call SDL_SyncWindow() to
2155
 * block until the changes have taken effect.
2156
 *
2157
 * When the window state changes, an SDL_EVENT_WINDOW_MINIMIZED event will be
2158
 * emitted. Note that, as this is just a request, the windowing system can
2159
 * deny the state change.
2160
 *
2161
 * \param window the window to minimize.
2162
 * \returns true on success or false on failure; call SDL_GetError() for more
2163
 *          information.
2164
 *
2165
 * \threadsafety This function should only be called on the main thread.
2166
 *
2167
 * \since This function is available since SDL 3.2.0.
2168
 *
2169
 * \sa SDL_MaximizeWindow
2170
 * \sa SDL_RestoreWindow
2171
 * \sa SDL_SyncWindow
2172
 */
2173
extern SDL_DECLSPEC bool SDLCALL SDL_MinimizeWindow(SDL_Window *window);
2174
2175
/**
2176
 * Request that the size and position of a minimized or maximized window be
2177
 * restored.
2178
 *
2179
 * If the window is in a fullscreen state, this request has no direct effect.
2180
 * It may alter the state the window is returned to when leaving fullscreen.
2181
 *
2182
 * On some windowing systems this request is asynchronous and the new window
2183
 * state may not have have been applied immediately upon the return of this
2184
 * function. If an immediate change is required, call SDL_SyncWindow() to
2185
 * block until the changes have taken effect.
2186
 *
2187
 * When the window state changes, an SDL_EVENT_WINDOW_RESTORED event will be
2188
 * emitted. Note that, as this is just a request, the windowing system can
2189
 * deny the state change.
2190
 *
2191
 * \param window the window to restore.
2192
 * \returns true on success or false on failure; call SDL_GetError() for more
2193
 *          information.
2194
 *
2195
 * \threadsafety This function should only be called on the main thread.
2196
 *
2197
 * \since This function is available since SDL 3.2.0.
2198
 *
2199
 * \sa SDL_MaximizeWindow
2200
 * \sa SDL_MinimizeWindow
2201
 * \sa SDL_SyncWindow
2202
 */
2203
extern SDL_DECLSPEC bool SDLCALL SDL_RestoreWindow(SDL_Window *window);
2204
2205
/**
2206
 * Request that the window's fullscreen state be changed.
2207
 *
2208
 * By default a window in fullscreen state uses borderless fullscreen desktop
2209
 * mode, but a specific exclusive display mode can be set using
2210
 * SDL_SetWindowFullscreenMode().
2211
 *
2212
 * On some windowing systems this request is asynchronous and the new
2213
 * fullscreen state may not have have been applied immediately upon the return
2214
 * of this function. If an immediate change is required, call SDL_SyncWindow()
2215
 * to block until the changes have taken effect.
2216
 *
2217
 * When the window state changes, an SDL_EVENT_WINDOW_ENTER_FULLSCREEN or
2218
 * SDL_EVENT_WINDOW_LEAVE_FULLSCREEN event will be emitted. Note that, as this
2219
 * is just a request, it can be denied by the windowing system.
2220
 *
2221
 * \param window the window to change.
2222
 * \param fullscreen true for fullscreen mode, false for windowed mode.
2223
 * \returns true on success or false on failure; call SDL_GetError() for more
2224
 *          information.
2225
 *
2226
 * \threadsafety This function should only be called on the main thread.
2227
 *
2228
 * \since This function is available since SDL 3.2.0.
2229
 *
2230
 * \sa SDL_GetWindowFullscreenMode
2231
 * \sa SDL_SetWindowFullscreenMode
2232
 * \sa SDL_SyncWindow
2233
 * \sa SDL_WINDOW_FULLSCREEN
2234
 */
2235
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFullscreen(SDL_Window *window, bool fullscreen);
2236
2237
/**
2238
 * Block until any pending window state is finalized.
2239
 *
2240
 * On asynchronous windowing systems, this acts as a synchronization barrier
2241
 * for pending window state. It will attempt to wait until any pending window
2242
 * state has been applied and is guaranteed to return within finite time. Note
2243
 * that for how long it can potentially block depends on the underlying window
2244
 * system, as window state changes may involve somewhat lengthy animations
2245
 * that must complete before the window is in its final requested state.
2246
 *
2247
 * On windowing systems where changes are immediate, this does nothing.
2248
 *
2249
 * \param window the window for which to wait for the pending state to be
2250
 *               applied.
2251
 * \returns true on success or false if the operation timed out before the
2252
 *          window was in the requested state.
2253
 *
2254
 * \threadsafety This function should only be called on the main thread.
2255
 *
2256
 * \since This function is available since SDL 3.2.0.
2257
 *
2258
 * \sa SDL_SetWindowSize
2259
 * \sa SDL_SetWindowPosition
2260
 * \sa SDL_SetWindowFullscreen
2261
 * \sa SDL_MinimizeWindow
2262
 * \sa SDL_MaximizeWindow
2263
 * \sa SDL_RestoreWindow
2264
 * \sa SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS
2265
 */
2266
extern SDL_DECLSPEC bool SDLCALL SDL_SyncWindow(SDL_Window *window);
2267
2268
/**
2269
 * Return whether the window has a surface associated with it.
2270
 *
2271
 * \param window the window to query.
2272
 * \returns true if there is a surface associated with the window, or false
2273
 *          otherwise.
2274
 *
2275
 * \threadsafety This function should only be called on the main thread.
2276
 *
2277
 * \since This function is available since SDL 3.2.0.
2278
 *
2279
 * \sa SDL_GetWindowSurface
2280
 */
2281
extern SDL_DECLSPEC bool SDLCALL SDL_WindowHasSurface(SDL_Window *window);
2282
2283
/**
2284
 * Get the SDL surface associated with the window.
2285
 *
2286
 * A new surface will be created with the optimal format for the window, if
2287
 * necessary. This surface will be freed when the window is destroyed. Do not
2288
 * free this surface.
2289
 *
2290
 * This surface will be invalidated if the window is resized. After resizing a
2291
 * window this function must be called again to return a valid surface.
2292
 *
2293
 * You may not combine this with 3D or the rendering API on this window.
2294
 *
2295
 * This function is affected by `SDL_HINT_FRAMEBUFFER_ACCELERATION`.
2296
 *
2297
 * \param window the window to query.
2298
 * \returns the surface associated with the window, or NULL on failure; call
2299
 *          SDL_GetError() for more information.
2300
 *
2301
 * \threadsafety This function should only be called on the main thread.
2302
 *
2303
 * \since This function is available since SDL 3.2.0.
2304
 *
2305
 * \sa SDL_DestroyWindowSurface
2306
 * \sa SDL_WindowHasSurface
2307
 * \sa SDL_UpdateWindowSurface
2308
 * \sa SDL_UpdateWindowSurfaceRects
2309
 */
2310
extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_GetWindowSurface(SDL_Window *window);
2311
2312
/**
2313
 * Toggle VSync for the window surface.
2314
 *
2315
 * When a window surface is created, vsync defaults to
2316
 * SDL_WINDOW_SURFACE_VSYNC_DISABLED.
2317
 *
2318
 * The `vsync` parameter can be 1 to synchronize present with every vertical
2319
 * refresh, 2 to synchronize present with every second vertical refresh, etc.,
2320
 * SDL_WINDOW_SURFACE_VSYNC_ADAPTIVE for late swap tearing (adaptive vsync),
2321
 * or SDL_WINDOW_SURFACE_VSYNC_DISABLED to disable. Not every value is
2322
 * supported by every driver, so you should check the return value to see
2323
 * whether the requested setting is supported.
2324
 *
2325
 * \param window the window.
2326
 * \param vsync the vertical refresh sync interval.
2327
 * \returns true on success or false on failure; call SDL_GetError() for more
2328
 *          information.
2329
 *
2330
 * \threadsafety This function should only be called on the main thread.
2331
 *
2332
 * \since This function is available since SDL 3.2.0.
2333
 *
2334
 * \sa SDL_GetWindowSurfaceVSync
2335
 */
2336
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowSurfaceVSync(SDL_Window *window, int vsync);
2337
2338
#define SDL_WINDOW_SURFACE_VSYNC_DISABLED 0
2339
#define SDL_WINDOW_SURFACE_VSYNC_ADAPTIVE (-1)
2340
2341
/**
2342
 * Get VSync for the window surface.
2343
 *
2344
 * \param window the window to query.
2345
 * \param vsync an int filled with the current vertical refresh sync interval.
2346
 *              See SDL_SetWindowSurfaceVSync() for the meaning of the value.
2347
 * \returns true on success or false on failure; call SDL_GetError() for more
2348
 *          information.
2349
 *
2350
 * \threadsafety This function should only be called on the main thread.
2351
 *
2352
 * \since This function is available since SDL 3.2.0.
2353
 *
2354
 * \sa SDL_SetWindowSurfaceVSync
2355
 */
2356
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowSurfaceVSync(SDL_Window *window, int *vsync);
2357
2358
/**
2359
 * Copy the window surface to the screen.
2360
 *
2361
 * This is the function you use to reflect any changes to the surface on the
2362
 * screen.
2363
 *
2364
 * This function is equivalent to the SDL 1.2 API SDL_Flip().
2365
 *
2366
 * \param window the window to update.
2367
 * \returns true on success or false on failure; call SDL_GetError() for more
2368
 *          information.
2369
 *
2370
 * \threadsafety This function should only be called on the main thread.
2371
 *
2372
 * \since This function is available since SDL 3.2.0.
2373
 *
2374
 * \sa SDL_GetWindowSurface
2375
 * \sa SDL_UpdateWindowSurfaceRects
2376
 */
2377
extern SDL_DECLSPEC bool SDLCALL SDL_UpdateWindowSurface(SDL_Window *window);
2378
2379
/**
2380
 * Copy areas of the window surface to the screen.
2381
 *
2382
 * This is the function you use to reflect changes to portions of the surface
2383
 * on the screen.
2384
 *
2385
 * This function is equivalent to the SDL 1.2 API SDL_UpdateRects().
2386
 *
2387
 * Note that this function will update _at least_ the rectangles specified,
2388
 * but this is only intended as an optimization; in practice, this might
2389
 * update more of the screen (or all of the screen!), depending on what method
2390
 * SDL uses to send pixels to the system.
2391
 *
2392
 * \param window the window to update.
2393
 * \param rects an array of SDL_Rect structures representing areas of the
2394
 *              surface to copy, in pixels.
2395
 * \param numrects the number of rectangles.
2396
 * \returns true on success or false on failure; call SDL_GetError() for more
2397
 *          information.
2398
 *
2399
 * \threadsafety This function should only be called on the main thread.
2400
 *
2401
 * \since This function is available since SDL 3.2.0.
2402
 *
2403
 * \sa SDL_GetWindowSurface
2404
 * \sa SDL_UpdateWindowSurface
2405
 */
2406
extern SDL_DECLSPEC bool SDLCALL SDL_UpdateWindowSurfaceRects(SDL_Window *window, const SDL_Rect *rects, int numrects);
2407
2408
/**
2409
 * Destroy the surface associated with the window.
2410
 *
2411
 * \param window the window to update.
2412
 * \returns true on success or false on failure; call SDL_GetError() for more
2413
 *          information.
2414
 *
2415
 * \threadsafety This function should only be called on the main thread.
2416
 *
2417
 * \since This function is available since SDL 3.2.0.
2418
 *
2419
 * \sa SDL_GetWindowSurface
2420
 * \sa SDL_WindowHasSurface
2421
 */
2422
extern SDL_DECLSPEC bool SDLCALL SDL_DestroyWindowSurface(SDL_Window *window);
2423
2424
/**
2425
 * Set a window's keyboard grab mode.
2426
 *
2427
 * Keyboard grab enables capture of system keyboard shortcuts like Alt+Tab or
2428
 * the Meta/Super key. Note that not all system keyboard shortcuts can be
2429
 * captured by applications (one example is Ctrl+Alt+Del on Windows).
2430
 *
2431
 * This is primarily intended for specialized applications such as VNC clients
2432
 * or VM frontends. Normal games should not use keyboard grab.
2433
 *
2434
 * When keyboard grab is enabled, SDL will continue to handle Alt+Tab when the
2435
 * window is full-screen to ensure the user is not trapped in your
2436
 * application. If you have a custom keyboard shortcut to exit fullscreen
2437
 * mode, you may suppress this behavior with
2438
 * `SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED`.
2439
 *
2440
 * If the caller enables a grab while another window is currently grabbed, the
2441
 * other window loses its grab in favor of the caller's window.
2442
 *
2443
 * \param window the window for which the keyboard grab mode should be set.
2444
 * \param grabbed this is true to grab keyboard, and false to release.
2445
 * \returns true on success or false on failure; call SDL_GetError() for more
2446
 *          information.
2447
 *
2448
 * \threadsafety This function should only be called on the main thread.
2449
 *
2450
 * \since This function is available since SDL 3.2.0.
2451
 *
2452
 * \sa SDL_GetWindowKeyboardGrab
2453
 * \sa SDL_SetWindowMouseGrab
2454
 */
2455
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowKeyboardGrab(SDL_Window *window, bool grabbed);
2456
2457
/**
2458
 * Set a window's mouse grab mode.
2459
 *
2460
 * Mouse grab confines the mouse cursor to the window.
2461
 *
2462
 * \param window the window for which the mouse grab mode should be set.
2463
 * \param grabbed this is true to grab mouse, and false to release.
2464
 * \returns true on success or false on failure; call SDL_GetError() for more
2465
 *          information.
2466
 *
2467
 * \threadsafety This function should only be called on the main thread.
2468
 *
2469
 * \since This function is available since SDL 3.2.0.
2470
 *
2471
 * \sa SDL_GetWindowMouseRect
2472
 * \sa SDL_SetWindowMouseRect
2473
 * \sa SDL_SetWindowMouseGrab
2474
 * \sa SDL_SetWindowKeyboardGrab
2475
 */
2476
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMouseGrab(SDL_Window *window, bool grabbed);
2477
2478
/**
2479
 * Get a window's keyboard grab mode.
2480
 *
2481
 * \param window the window to query.
2482
 * \returns true if keyboard is grabbed, and false otherwise.
2483
 *
2484
 * \threadsafety This function should only be called on the main thread.
2485
 *
2486
 * \since This function is available since SDL 3.2.0.
2487
 *
2488
 * \sa SDL_SetWindowKeyboardGrab
2489
 */
2490
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowKeyboardGrab(SDL_Window *window);
2491
2492
/**
2493
 * Get a window's mouse grab mode.
2494
 *
2495
 * \param window the window to query.
2496
 * \returns true if mouse is grabbed, and false otherwise.
2497
 *
2498
 * \threadsafety This function should only be called on the main thread.
2499
 *
2500
 * \since This function is available since SDL 3.2.0.
2501
 *
2502
 * \sa SDL_GetWindowMouseRect
2503
 * \sa SDL_SetWindowMouseRect
2504
 * \sa SDL_SetWindowMouseGrab
2505
 * \sa SDL_SetWindowKeyboardGrab
2506
 */
2507
extern SDL_DECLSPEC bool SDLCALL SDL_GetWindowMouseGrab(SDL_Window *window);
2508
2509
/**
2510
 * Get the window that currently has an input grab enabled.
2511
 *
2512
 * \returns the window if input is grabbed or NULL otherwise.
2513
 *
2514
 * \threadsafety This function should only be called on the main thread.
2515
 *
2516
 * \since This function is available since SDL 3.2.0.
2517
 *
2518
 * \sa SDL_SetWindowMouseGrab
2519
 * \sa SDL_SetWindowKeyboardGrab
2520
 */
2521
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetGrabbedWindow(void);
2522
2523
/**
2524
 * Confines the cursor to the specified area of a window.
2525
 *
2526
 * Note that this does NOT grab the cursor, it only defines the area a cursor
2527
 * is restricted to when the window has mouse focus.
2528
 *
2529
 * \param window the window that will be associated with the barrier.
2530
 * \param rect a rectangle area in window-relative coordinates. If NULL the
2531
 *             barrier for the specified window will be destroyed.
2532
 * \returns true on success or false on failure; call SDL_GetError() for more
2533
 *          information.
2534
 *
2535
 * \threadsafety This function should only be called on the main thread.
2536
 *
2537
 * \since This function is available since SDL 3.2.0.
2538
 *
2539
 * \sa SDL_GetWindowMouseRect
2540
 * \sa SDL_GetWindowMouseGrab
2541
 * \sa SDL_SetWindowMouseGrab
2542
 */
2543
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowMouseRect(SDL_Window *window, const SDL_Rect *rect);
2544
2545
/**
2546
 * Get the mouse confinement rectangle of a window.
2547
 *
2548
 * \param window the window to query.
2549
 * \returns a pointer to the mouse confinement rectangle of a window, or NULL
2550
 *          if there isn't one.
2551
 *
2552
 * \threadsafety This function should only be called on the main thread.
2553
 *
2554
 * \since This function is available since SDL 3.2.0.
2555
 *
2556
 * \sa SDL_SetWindowMouseRect
2557
 * \sa SDL_GetWindowMouseGrab
2558
 * \sa SDL_SetWindowMouseGrab
2559
 */
2560
extern SDL_DECLSPEC const SDL_Rect * SDLCALL SDL_GetWindowMouseRect(SDL_Window *window);
2561
2562
/**
2563
 * Set the opacity for a window.
2564
 *
2565
 * The parameter `opacity` will be clamped internally between 0.0f
2566
 * (transparent) and 1.0f (opaque).
2567
 *
2568
 * This function also returns false if setting the opacity isn't supported.
2569
 *
2570
 * \param window the window which will be made transparent or opaque.
2571
 * \param opacity the opacity value (0.0f - transparent, 1.0f - opaque).
2572
 * \returns true on success or false on failure; call SDL_GetError() for more
2573
 *          information.
2574
 *
2575
 * \threadsafety This function should only be called on the main thread.
2576
 *
2577
 * \since This function is available since SDL 3.2.0.
2578
 *
2579
 * \sa SDL_GetWindowOpacity
2580
 */
2581
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowOpacity(SDL_Window *window, float opacity);
2582
2583
/**
2584
 * Get the opacity of a window.
2585
 *
2586
 * If transparency isn't supported on this platform, opacity will be returned
2587
 * as 1.0f without error.
2588
 *
2589
 * \param window the window to get the current opacity value from.
2590
 * \returns the opacity, (0.0f - transparent, 1.0f - opaque), or -1.0f on
2591
 *          failure; call SDL_GetError() for more information.
2592
 *
2593
 * \threadsafety This function should only be called on the main thread.
2594
 *
2595
 * \since This function is available since SDL 3.2.0.
2596
 *
2597
 * \sa SDL_SetWindowOpacity
2598
 */
2599
extern SDL_DECLSPEC float SDLCALL SDL_GetWindowOpacity(SDL_Window *window);
2600
2601
/**
2602
 * Set the window as a child of a parent window.
2603
 *
2604
 * If the window is already the child of an existing window, it will be
2605
 * reparented to the new owner. Setting the parent window to NULL unparents
2606
 * the window and removes child window status.
2607
 *
2608
 * If a parent window is hidden or destroyed, the operation will be
2609
 * recursively applied to child windows. Child windows hidden with the parent
2610
 * that did not have their hidden status explicitly set will be restored when
2611
 * the parent is shown.
2612
 *
2613
 * Attempting to set the parent of a window that is currently in the modal
2614
 * state will fail. Use SDL_SetWindowModal() to cancel the modal status before
2615
 * attempting to change the parent.
2616
 *
2617
 * Popup windows cannot change parents and attempts to do so will fail.
2618
 *
2619
 * Setting a parent window that is currently the sibling or descendent of the
2620
 * child window results in undefined behavior.
2621
 *
2622
 * \param window the window that should become the child of a parent.
2623
 * \param parent the new parent window for the child window.
2624
 * \returns true on success or false on failure; call SDL_GetError() for more
2625
 *          information.
2626
 *
2627
 * \threadsafety This function should only be called on the main thread.
2628
 *
2629
 * \since This function is available since SDL 3.2.0.
2630
 *
2631
 * \sa SDL_SetWindowModal
2632
 */
2633
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent);
2634
2635
/**
2636
 * Toggle the state of the window as modal.
2637
 *
2638
 * To enable modal status on a window, the window must currently be the child
2639
 * window of a parent, or toggling modal status on will fail.
2640
 *
2641
 * \param window the window on which to set the modal state.
2642
 * \param modal true to toggle modal status on, false to toggle it off.
2643
 * \returns true on success or false on failure; call SDL_GetError() for more
2644
 *          information.
2645
 *
2646
 * \threadsafety This function should only be called on the main thread.
2647
 *
2648
 * \since This function is available since SDL 3.2.0.
2649
 *
2650
 * \sa SDL_SetWindowParent
2651
 * \sa SDL_WINDOW_MODAL
2652
 */
2653
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowModal(SDL_Window *window, bool modal);
2654
2655
/**
2656
 * Set whether the window may have input focus.
2657
 *
2658
 * \param window the window to set focusable state.
2659
 * \param focusable true to allow input focus, false to not allow input focus.
2660
 * \returns true on success or false on failure; call SDL_GetError() for more
2661
 *          information.
2662
 *
2663
 * \threadsafety This function should only be called on the main thread.
2664
 *
2665
 * \since This function is available since SDL 3.2.0.
2666
 */
2667
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowFocusable(SDL_Window *window, bool focusable);
2668
2669
2670
/**
2671
 * Display the system-level window menu.
2672
 *
2673
 * This default window menu is provided by the system and on some platforms
2674
 * provides functionality for setting or changing privileged state on the
2675
 * window, such as moving it between workspaces or displays, or toggling the
2676
 * always-on-top property.
2677
 *
2678
 * On platforms or desktops where this is unsupported, this function does
2679
 * nothing.
2680
 *
2681
 * \param window the window for which the menu will be displayed.
2682
 * \param x the x coordinate of the menu, relative to the origin (top-left) of
2683
 *          the client area.
2684
 * \param y the y coordinate of the menu, relative to the origin (top-left) of
2685
 *          the client area.
2686
 * \returns true on success or false on failure; call SDL_GetError() for more
2687
 *          information.
2688
 *
2689
 * \threadsafety This function should only be called on the main thread.
2690
 *
2691
 * \since This function is available since SDL 3.2.0.
2692
 */
2693
extern SDL_DECLSPEC bool SDLCALL SDL_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
2694
2695
/**
2696
 * Possible return values from the SDL_HitTest callback.
2697
 *
2698
 * \threadsafety This function should only be called on the main thread.
2699
 *
2700
 * \since This enum is available since SDL 3.2.0.
2701
 *
2702
 * \sa SDL_HitTest
2703
 */
2704
typedef enum SDL_HitTestResult
2705
{
2706
    SDL_HITTEST_NORMAL,             /**< Region is normal. No special properties. */
2707
    SDL_HITTEST_DRAGGABLE,          /**< Region can drag entire window. */
2708
    SDL_HITTEST_RESIZE_TOPLEFT,     /**< Region is the resizable top-left corner border. */
2709
    SDL_HITTEST_RESIZE_TOP,         /**< Region is the resizable top border. */
2710
    SDL_HITTEST_RESIZE_TOPRIGHT,    /**< Region is the resizable top-right corner border. */
2711
    SDL_HITTEST_RESIZE_RIGHT,       /**< Region is the resizable right border. */
2712
    SDL_HITTEST_RESIZE_BOTTOMRIGHT, /**< Region is the resizable bottom-right corner border. */
2713
    SDL_HITTEST_RESIZE_BOTTOM,      /**< Region is the resizable bottom border. */
2714
    SDL_HITTEST_RESIZE_BOTTOMLEFT,  /**< Region is the resizable bottom-left corner border. */
2715
    SDL_HITTEST_RESIZE_LEFT         /**< Region is the resizable left border. */
2716
} SDL_HitTestResult;
2717
2718
/**
2719
 * Callback used for hit-testing.
2720
 *
2721
 * \param win the SDL_Window where hit-testing was set on.
2722
 * \param area an SDL_Point which should be hit-tested.
2723
 * \param data what was passed as `callback_data` to SDL_SetWindowHitTest().
2724
 * \returns an SDL_HitTestResult value.
2725
 *
2726
 * \sa SDL_SetWindowHitTest
2727
 */
2728
typedef SDL_HitTestResult (SDLCALL *SDL_HitTest)(SDL_Window *win,
2729
                                                 const SDL_Point *area,
2730
                                                 void *data);
2731
2732
/**
2733
 * Provide a callback that decides if a window region has special properties.
2734
 *
2735
 * Normally windows are dragged and resized by decorations provided by the
2736
 * system window manager (a title bar, borders, etc), but for some apps, it
2737
 * makes sense to drag them from somewhere else inside the window itself; for
2738
 * example, one might have a borderless window that wants to be draggable from
2739
 * any part, or simulate its own title bar, etc.
2740
 *
2741
 * This function lets the app provide a callback that designates pieces of a
2742
 * given window as special. This callback is run during event processing if we
2743
 * need to tell the OS to treat a region of the window specially; the use of
2744
 * this callback is known as "hit testing."
2745
 *
2746
 * Mouse input may not be delivered to your application if it is within a
2747
 * special area; the OS will often apply that input to moving the window or
2748
 * resizing the window and not deliver it to the application.
2749
 *
2750
 * Specifying NULL for a callback disables hit-testing. Hit-testing is
2751
 * disabled by default.
2752
 *
2753
 * Platforms that don't support this functionality will return false
2754
 * unconditionally, even if you're attempting to disable hit-testing.
2755
 *
2756
 * Your callback may fire at any time, and its firing does not indicate any
2757
 * specific behavior (for example, on Windows, this certainly might fire when
2758
 * the OS is deciding whether to drag your window, but it fires for lots of
2759
 * other reasons, too, some unrelated to anything you probably care about _and
2760
 * when the mouse isn't actually at the location it is testing_). Since this
2761
 * can fire at any time, you should try to keep your callback efficient,
2762
 * devoid of allocations, etc.
2763
 *
2764
 * \param window the window to set hit-testing on.
2765
 * \param callback the function to call when doing a hit-test.
2766
 * \param callback_data an app-defined void pointer passed to **callback**.
2767
 * \returns true on success or false on failure; call SDL_GetError() for more
2768
 *          information.
2769
 *
2770
 * \threadsafety This function should only be called on the main thread.
2771
 *
2772
 * \since This function is available since SDL 3.2.0.
2773
 */
2774
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowHitTest(SDL_Window *window, SDL_HitTest callback, void *callback_data);
2775
2776
/**
2777
 * Set the shape of a transparent window.
2778
 *
2779
 * This sets the alpha channel of a transparent window and any fully
2780
 * transparent areas are also transparent to mouse clicks. If you are using
2781
 * something besides the SDL render API, then you are responsible for drawing
2782
 * the alpha channel of the window to match the shape alpha channel to get
2783
 * consistent cross-platform results.
2784
 *
2785
 * The shape is copied inside this function, so you can free it afterwards. If
2786
 * your shape surface changes, you should call SDL_SetWindowShape() again to
2787
 * update the window. This is an expensive operation, so should be done
2788
 * sparingly.
2789
 *
2790
 * The window must have been created with the SDL_WINDOW_TRANSPARENT flag.
2791
 *
2792
 * \param window the window.
2793
 * \param shape the surface representing the shape of the window, or NULL to
2794
 *              remove any current shape.
2795
 * \returns true on success or false on failure; call SDL_GetError() for more
2796
 *          information.
2797
 *
2798
 * \threadsafety This function should only be called on the main thread.
2799
 *
2800
 * \since This function is available since SDL 3.2.0.
2801
 */
2802
extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowShape(SDL_Window *window, SDL_Surface *shape);
2803
2804
/**
2805
 * Request a window to demand attention from the user.
2806
 *
2807
 * \param window the window to be flashed.
2808
 * \param operation the operation to perform.
2809
 * \returns true on success or false on failure; call SDL_GetError() for more
2810
 *          information.
2811
 *
2812
 * \threadsafety This function should only be called on the main thread.
2813
 *
2814
 * \since This function is available since SDL 3.2.0.
2815
 */
2816
extern SDL_DECLSPEC bool SDLCALL SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation);
2817
2818
/**
2819
 * Destroy a window.
2820
 *
2821
 * Any child windows owned by the window will be recursively destroyed as
2822
 * well.
2823
 *
2824
 * Note that on some platforms, the visible window may not actually be removed
2825
 * from the screen until the SDL event loop is pumped again, even though the
2826
 * SDL_Window is no longer valid after this call.
2827
 *
2828
 * \param window the window to destroy.
2829
 *
2830
 * \threadsafety This function should only be called on the main thread.
2831
 *
2832
 * \since This function is available since SDL 3.2.0.
2833
 *
2834
 * \sa SDL_CreatePopupWindow
2835
 * \sa SDL_CreateWindow
2836
 * \sa SDL_CreateWindowWithProperties
2837
 */
2838
extern SDL_DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window *window);
2839
2840
2841
/**
2842
 * Check whether the screensaver is currently enabled.
2843
 *
2844
 * The screensaver is disabled by default.
2845
 *
2846
 * The default can also be changed using `SDL_HINT_VIDEO_ALLOW_SCREENSAVER`.
2847
 *
2848
 * \returns true if the screensaver is enabled, false if it is disabled.
2849
 *
2850
 * \threadsafety This function should only be called on the main thread.
2851
 *
2852
 * \since This function is available since SDL 3.2.0.
2853
 *
2854
 * \sa SDL_DisableScreenSaver
2855
 * \sa SDL_EnableScreenSaver
2856
 */
2857
extern SDL_DECLSPEC bool SDLCALL SDL_ScreenSaverEnabled(void);
2858
2859
/**
2860
 * Allow the screen to be blanked by a screen saver.
2861
 *
2862
 * \returns true on success or false on failure; call SDL_GetError() for more
2863
 *          information.
2864
 *
2865
 * \threadsafety This function should only be called on the main thread.
2866
 *
2867
 * \since This function is available since SDL 3.2.0.
2868
 *
2869
 * \sa SDL_DisableScreenSaver
2870
 * \sa SDL_ScreenSaverEnabled
2871
 */
2872
extern SDL_DECLSPEC bool SDLCALL SDL_EnableScreenSaver(void);
2873
2874
/**
2875
 * Prevent the screen from being blanked by a screen saver.
2876
 *
2877
 * If you disable the screensaver, it is automatically re-enabled when SDL
2878
 * quits.
2879
 *
2880
 * The screensaver is disabled by default, but this may by changed by
2881
 * SDL_HINT_VIDEO_ALLOW_SCREENSAVER.
2882
 *
2883
 * \returns true on success or false on failure; call SDL_GetError() for more
2884
 *          information.
2885
 *
2886
 * \threadsafety This function should only be called on the main thread.
2887
 *
2888
 * \since This function is available since SDL 3.2.0.
2889
 *
2890
 * \sa SDL_EnableScreenSaver
2891
 * \sa SDL_ScreenSaverEnabled
2892
 */
2893
extern SDL_DECLSPEC bool SDLCALL SDL_DisableScreenSaver(void);
2894
2895
2896
/**
2897
 *  \name OpenGL support functions
2898
 */
2899
/* @{ */
2900
2901
/**
2902
 * Dynamically load an OpenGL library.
2903
 *
2904
 * This should be done after initializing the video driver, but before
2905
 * creating any OpenGL windows. If no OpenGL library is loaded, the default
2906
 * library will be loaded upon creation of the first OpenGL window.
2907
 *
2908
 * If you do this, you need to retrieve all of the GL functions used in your
2909
 * program from the dynamic library using SDL_GL_GetProcAddress().
2910
 *
2911
 * \param path the platform dependent OpenGL library name, or NULL to open the
2912
 *             default OpenGL library.
2913
 * \returns true on success or false on failure; call SDL_GetError() for more
2914
 *          information.
2915
 *
2916
 * \threadsafety This function should only be called on the main thread.
2917
 *
2918
 * \since This function is available since SDL 3.2.0.
2919
 *
2920
 * \sa SDL_GL_GetProcAddress
2921
 * \sa SDL_GL_UnloadLibrary
2922
 */
2923
extern SDL_DECLSPEC bool SDLCALL SDL_GL_LoadLibrary(const char *path);
2924
2925
/**
2926
 * Get an OpenGL function by name.
2927
 *
2928
 * If the GL library is loaded at runtime with SDL_GL_LoadLibrary(), then all
2929
 * GL functions must be retrieved this way. Usually this is used to retrieve
2930
 * function pointers to OpenGL extensions.
2931
 *
2932
 * There are some quirks to looking up OpenGL functions that require some
2933
 * extra care from the application. If you code carefully, you can handle
2934
 * these quirks without any platform-specific code, though:
2935
 *
2936
 * - On Windows, function pointers are specific to the current GL context;
2937
 *   this means you need to have created a GL context and made it current
2938
 *   before calling SDL_GL_GetProcAddress(). If you recreate your context or
2939
 *   create a second context, you should assume that any existing function
2940
 *   pointers aren't valid to use with it. This is (currently) a
2941
 *   Windows-specific limitation, and in practice lots of drivers don't suffer
2942
 *   this limitation, but it is still the way the wgl API is documented to
2943
 *   work and you should expect crashes if you don't respect it. Store a copy
2944
 *   of the function pointers that comes and goes with context lifespan.
2945
 * - On X11, function pointers returned by this function are valid for any
2946
 *   context, and can even be looked up before a context is created at all.
2947
 *   This means that, for at least some common OpenGL implementations, if you
2948
 *   look up a function that doesn't exist, you'll get a non-NULL result that
2949
 *   is _NOT_ safe to call. You must always make sure the function is actually
2950
 *   available for a given GL context before calling it, by checking for the
2951
 *   existence of the appropriate extension with SDL_GL_ExtensionSupported(),
2952
 *   or verifying that the version of OpenGL you're using offers the function
2953
 *   as core functionality.
2954
 * - Some OpenGL drivers, on all platforms, *will* return NULL if a function
2955
 *   isn't supported, but you can't count on this behavior. Check for
2956
 *   extensions you use, and if you get a NULL anyway, act as if that
2957
 *   extension wasn't available. This is probably a bug in the driver, but you
2958
 *   can code defensively for this scenario anyhow.
2959
 * - Just because you're on Linux/Unix, don't assume you'll be using X11.
2960
 *   Next-gen display servers are waiting to replace it, and may or may not
2961
 *   make the same promises about function pointers.
2962
 * - OpenGL function pointers must be declared `APIENTRY` as in the example
2963
 *   code. This will ensure the proper calling convention is followed on
2964
 *   platforms where this matters (Win32) thereby avoiding stack corruption.
2965
 *
2966
 * \param proc the name of an OpenGL function.
2967
 * \returns a pointer to the named OpenGL function. The returned pointer
2968
 *          should be cast to the appropriate function signature.
2969
 *
2970
 * \threadsafety This function should only be called on the main thread.
2971
 *
2972
 * \since This function is available since SDL 3.2.0.
2973
 *
2974
 * \sa SDL_GL_ExtensionSupported
2975
 * \sa SDL_GL_LoadLibrary
2976
 * \sa SDL_GL_UnloadLibrary
2977
 */
2978
extern SDL_DECLSPEC SDL_FunctionPointer SDLCALL SDL_GL_GetProcAddress(const char *proc);
2979
2980
/**
2981
 * Get an EGL library function by name.
2982
 *
2983
 * If an EGL library is loaded, this function allows applications to get entry
2984
 * points for EGL functions. This is useful to provide to an EGL API and
2985
 * extension loader.
2986
 *
2987
 * \param proc the name of an EGL function.
2988
 * \returns a pointer to the named EGL function. The returned pointer should
2989
 *          be cast to the appropriate function signature.
2990
 *
2991
 * \threadsafety This function should only be called on the main thread.
2992
 *
2993
 * \since This function is available since SDL 3.2.0.
2994
 *
2995
 * \sa SDL_EGL_GetCurrentDisplay
2996
 */
2997
extern SDL_DECLSPEC SDL_FunctionPointer SDLCALL SDL_EGL_GetProcAddress(const char *proc);
2998
2999
/**
3000
 * Unload the OpenGL library previously loaded by SDL_GL_LoadLibrary().
3001
 *
3002
 * \threadsafety This function should only be called on the main thread.
3003
 *
3004
 * \since This function is available since SDL 3.2.0.
3005
 *
3006
 * \sa SDL_GL_LoadLibrary
3007
 */
3008
extern SDL_DECLSPEC void SDLCALL SDL_GL_UnloadLibrary(void);
3009
3010
/**
3011
 * Check if an OpenGL extension is supported for the current context.
3012
 *
3013
 * This function operates on the current GL context; you must have created a
3014
 * context and it must be current before calling this function. Do not assume
3015
 * that all contexts you create will have the same set of extensions
3016
 * available, or that recreating an existing context will offer the same
3017
 * extensions again.
3018
 *
3019
 * While it's probably not a massive overhead, this function is not an O(1)
3020
 * operation. Check the extensions you care about after creating the GL
3021
 * context and save that information somewhere instead of calling the function
3022
 * every time you need to know.
3023
 *
3024
 * \param extension the name of the extension to check.
3025
 * \returns true if the extension is supported, false otherwise.
3026
 *
3027
 * \threadsafety This function should only be called on the main thread.
3028
 *
3029
 * \since This function is available since SDL 3.2.0.
3030
 */
3031
extern SDL_DECLSPEC bool SDLCALL SDL_GL_ExtensionSupported(const char *extension);
3032
3033
/**
3034
 * Reset all previously set OpenGL context attributes to their default values.
3035
 *
3036
 * \threadsafety This function should only be called on the main thread.
3037
 *
3038
 * \since This function is available since SDL 3.2.0.
3039
 *
3040
 * \sa SDL_GL_GetAttribute
3041
 * \sa SDL_GL_SetAttribute
3042
 */
3043
extern SDL_DECLSPEC void SDLCALL SDL_GL_ResetAttributes(void);
3044
3045
/**
3046
 * Set an OpenGL window attribute before window creation.
3047
 *
3048
 * This function sets the OpenGL attribute `attr` to `value`. The requested
3049
 * attributes should be set before creating an OpenGL window. You should use
3050
 * SDL_GL_GetAttribute() to check the values after creating the OpenGL
3051
 * context, since the values obtained can differ from the requested ones.
3052
 *
3053
 * \param attr an SDL_GLAttr enum value specifying the OpenGL attribute to
3054
 *             set.
3055
 * \param value the desired value for the attribute.
3056
 * \returns true on success or false on failure; call SDL_GetError() for more
3057
 *          information.
3058
 *
3059
 * \threadsafety This function should only be called on the main thread.
3060
 *
3061
 * \since This function is available since SDL 3.2.0.
3062
 *
3063
 * \sa SDL_GL_GetAttribute
3064
 * \sa SDL_GL_ResetAttributes
3065
 */
3066
extern SDL_DECLSPEC bool SDLCALL SDL_GL_SetAttribute(SDL_GLAttr attr, int value);
3067
3068
/**
3069
 * Get the actual value for an attribute from the current context.
3070
 *
3071
 * \param attr an SDL_GLAttr enum value specifying the OpenGL attribute to
3072
 *             get.
3073
 * \param value a pointer filled in with the current value of `attr`.
3074
 * \returns true on success or false on failure; call SDL_GetError() for more
3075
 *          information.
3076
 *
3077
 * \threadsafety This function should only be called on the main thread.
3078
 *
3079
 * \since This function is available since SDL 3.2.0.
3080
 *
3081
 * \sa SDL_GL_ResetAttributes
3082
 * \sa SDL_GL_SetAttribute
3083
 */
3084
extern SDL_DECLSPEC bool SDLCALL SDL_GL_GetAttribute(SDL_GLAttr attr, int *value);
3085
3086
/**
3087
 * Create an OpenGL context for an OpenGL window, and make it current.
3088
 *
3089
 * Windows users new to OpenGL should note that, for historical reasons, GL
3090
 * functions added after OpenGL version 1.1 are not available by default.
3091
 * Those functions must be loaded at run-time, either with an OpenGL
3092
 * extension-handling library or with SDL_GL_GetProcAddress() and its related
3093
 * functions.
3094
 *
3095
 * SDL_GLContext is opaque to the application.
3096
 *
3097
 * \param window the window to associate with the context.
3098
 * \returns the OpenGL context associated with `window` or NULL on failure;
3099
 *          call SDL_GetError() for more information.
3100
 *
3101
 * \threadsafety This function should only be called on the main thread.
3102
 *
3103
 * \since This function is available since SDL 3.2.0.
3104
 *
3105
 * \sa SDL_GL_DestroyContext
3106
 * \sa SDL_GL_MakeCurrent
3107
 */
3108
extern SDL_DECLSPEC SDL_GLContext SDLCALL SDL_GL_CreateContext(SDL_Window *window);
3109
3110
/**
3111
 * Set up an OpenGL context for rendering into an OpenGL window.
3112
 *
3113
 * The context must have been created with a compatible window.
3114
 *
3115
 * \param window the window to associate with the context.
3116
 * \param context the OpenGL context to associate with the window.
3117
 * \returns true on success or false on failure; call SDL_GetError() for more
3118
 *          information.
3119
 *
3120
 * \threadsafety This function should only be called on the main thread.
3121
 *
3122
 * \since This function is available since SDL 3.2.0.
3123
 *
3124
 * \sa SDL_GL_CreateContext
3125
 */
3126
extern SDL_DECLSPEC bool SDLCALL SDL_GL_MakeCurrent(SDL_Window *window, SDL_GLContext context);
3127
3128
/**
3129
 * Get the currently active OpenGL window.
3130
 *
3131
 * \returns the currently active OpenGL window on success or NULL on failure;
3132
 *          call SDL_GetError() for more information.
3133
 *
3134
 * \threadsafety This function should only be called on the main thread.
3135
 *
3136
 * \since This function is available since SDL 3.2.0.
3137
 */
3138
extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GL_GetCurrentWindow(void);
3139
3140
/**
3141
 * Get the currently active OpenGL context.
3142
 *
3143
 * \returns the currently active OpenGL context or NULL on failure; call
3144
 *          SDL_GetError() for more information.
3145
 *
3146
 * \threadsafety This function should only be called on the main thread.
3147
 *
3148
 * \since This function is available since SDL 3.2.0.
3149
 *
3150
 * \sa SDL_GL_MakeCurrent
3151
 */
3152
extern SDL_DECLSPEC SDL_GLContext SDLCALL SDL_GL_GetCurrentContext(void);
3153
3154
/**
3155
 * Get the currently active EGL display.
3156
 *
3157
 * \returns the currently active EGL display or NULL on failure; call
3158
 *          SDL_GetError() for more information.
3159
 *
3160
 * \threadsafety This function should only be called on the main thread.
3161
 *
3162
 * \since This function is available since SDL 3.2.0.
3163
 */
3164
extern SDL_DECLSPEC SDL_EGLDisplay SDLCALL SDL_EGL_GetCurrentDisplay(void);
3165
3166
/**
3167
 * Get the currently active EGL config.
3168
 *
3169
 * \returns the currently active EGL config or NULL on failure; call
3170
 *          SDL_GetError() for more information.
3171
 *
3172
 * \threadsafety This function should only be called on the main thread.
3173
 *
3174
 * \since This function is available since SDL 3.2.0.
3175
 */
3176
extern SDL_DECLSPEC SDL_EGLConfig SDLCALL SDL_EGL_GetCurrentConfig(void);
3177
3178
/**
3179
 * Get the EGL surface associated with the window.
3180
 *
3181
 * \param window the window to query.
3182
 * \returns the EGLSurface pointer associated with the window, or NULL on
3183
 *          failure.
3184
 *
3185
 * \threadsafety This function should only be called on the main thread.
3186
 *
3187
 * \since This function is available since SDL 3.2.0.
3188
 */
3189
extern SDL_DECLSPEC SDL_EGLSurface SDLCALL SDL_EGL_GetWindowSurface(SDL_Window *window);
3190
3191
/**
3192
 * Sets the callbacks for defining custom EGLAttrib arrays for EGL
3193
 * initialization.
3194
 *
3195
 * Callbacks that aren't needed can be set to NULL.
3196
 *
3197
 * NOTE: These callback pointers will be reset after SDL_GL_ResetAttributes.
3198
 *
3199
 * \param platformAttribCallback callback for attributes to pass to
3200
 *                               eglGetPlatformDisplay. May be NULL.
3201
 * \param surfaceAttribCallback callback for attributes to pass to
3202
 *                              eglCreateSurface. May be NULL.
3203
 * \param contextAttribCallback callback for attributes to pass to
3204
 *                              eglCreateContext. May be NULL.
3205
 * \param userdata a pointer that is passed to the callbacks.
3206
 *
3207
 * \threadsafety This function should only be called on the main thread.
3208
 *
3209
 * \since This function is available since SDL 3.2.0.
3210
 */
3211
extern SDL_DECLSPEC void SDLCALL SDL_EGL_SetAttributeCallbacks(SDL_EGLAttribArrayCallback platformAttribCallback,
3212
                                                               SDL_EGLIntArrayCallback surfaceAttribCallback,
3213
                                                               SDL_EGLIntArrayCallback contextAttribCallback, void *userdata);
3214
3215
/**
3216
 * Set the swap interval for the current OpenGL context.
3217
 *
3218
 * Some systems allow specifying -1 for the interval, to enable adaptive
3219
 * vsync. Adaptive vsync works the same as vsync, but if you've already missed
3220
 * the vertical retrace for a given frame, it swaps buffers immediately, which
3221
 * might be less jarring for the user during occasional framerate drops. If an
3222
 * application requests adaptive vsync and the system does not support it,
3223
 * this function will fail and return false. In such a case, you should
3224
 * probably retry the call with 1 for the interval.
3225
 *
3226
 * Adaptive vsync is implemented for some glX drivers with
3227
 * GLX_EXT_swap_control_tear, and for some Windows drivers with
3228
 * WGL_EXT_swap_control_tear.
3229
 *
3230
 * Read more on the Khronos wiki:
3231
 * https://www.khronos.org/opengl/wiki/Swap_Interval#Adaptive_Vsync
3232
 *
3233
 * \param interval 0 for immediate updates, 1 for updates synchronized with
3234
 *                 the vertical retrace, -1 for adaptive vsync.
3235
 * \returns true on success or false on failure; call SDL_GetError() for more
3236
 *          information.
3237
 *
3238
 * \threadsafety This function should only be called on the main thread.
3239
 *
3240
 * \since This function is available since SDL 3.2.0.
3241
 *
3242
 * \sa SDL_GL_GetSwapInterval
3243
 */
3244
extern SDL_DECLSPEC bool SDLCALL SDL_GL_SetSwapInterval(int interval);
3245
3246
/**
3247
 * Get the swap interval for the current OpenGL context.
3248
 *
3249
 * If the system can't determine the swap interval, or there isn't a valid
3250
 * current context, this function will set *interval to 0 as a safe default.
3251
 *
3252
 * \param interval output interval value. 0 if there is no vertical retrace
3253
 *                 synchronization, 1 if the buffer swap is synchronized with
3254
 *                 the vertical retrace, and -1 if late swaps happen
3255
 *                 immediately instead of waiting for the next retrace.
3256
 * \returns true on success or false on failure; call SDL_GetError() for more
3257
 *          information.
3258
 *
3259
 * \threadsafety This function should only be called on the main thread.
3260
 *
3261
 * \since This function is available since SDL 3.2.0.
3262
 *
3263
 * \sa SDL_GL_SetSwapInterval
3264
 */
3265
extern SDL_DECLSPEC bool SDLCALL SDL_GL_GetSwapInterval(int *interval);
3266
3267
/**
3268
 * Update a window with OpenGL rendering.
3269
 *
3270
 * This is used with double-buffered OpenGL contexts, which are the default.
3271
 *
3272
 * On macOS, make sure you bind 0 to the draw framebuffer before swapping the
3273
 * window, otherwise nothing will happen. If you aren't using
3274
 * glBindFramebuffer(), this is the default and you won't have to do anything
3275
 * extra.
3276
 *
3277
 * \param window the window to change.
3278
 * \returns true on success or false on failure; call SDL_GetError() for more
3279
 *          information.
3280
 *
3281
 * \threadsafety This function should only be called on the main thread.
3282
 *
3283
 * \since This function is available since SDL 3.2.0.
3284
 */
3285
extern SDL_DECLSPEC bool SDLCALL SDL_GL_SwapWindow(SDL_Window *window);
3286
3287
/**
3288
 * Delete an OpenGL context.
3289
 *
3290
 * \param context the OpenGL context to be deleted.
3291
 * \returns true on success or false on failure; call SDL_GetError() for more
3292
 *          information.
3293
 *
3294
 * \threadsafety This function should only be called on the main thread.
3295
 *
3296
 * \since This function is available since SDL 3.2.0.
3297
 *
3298
 * \sa SDL_GL_CreateContext
3299
 */
3300
extern SDL_DECLSPEC bool SDLCALL SDL_GL_DestroyContext(SDL_GLContext context);
3301
3302
/* @} *//* OpenGL support functions */
3303
3304
3305
/* Ends C function definitions when using C++ */
3306
#ifdef __cplusplus
3307
}
3308
#endif
3309
#include <SDL3/SDL_close_code.h>
3310
3311
#endif /* SDL_video_h_ */